面试题:RangeList的实现思路

782 阅读2分钟

今天蹲坑的时候看到一个面试题RangeList,实现一个类可以add,remove任意区间,交叉的区间会何必在一起,测试用例如下

const rl = new RangeList();
rl.add([1, 5]);
rl.print();// Should display: [1, 5)
rl.add([10, 20]);
rl.print();// Should display: [1, 5) [10, 20)
rl.add([20, 20]);
rl.print();// Should display: [1, 5) [10, 20)

花了一个小时,整理了一下我的想法:

需要枚举的情况就是要加入或者移除的start和end在已有的range的关系,大致可分为四种

  • start在A区间内,end在B区间内,A和B相差N个区间
  • start在A区间内,end在B和C的区间断层处,A和C相差N个区间
  • start在B和C的区间断层处,end在D区间内,B和D相差N个区间
  • start在B和C的区间断层处,end在D和E区间断层处,B和E相差N个区间 其中N是大于等于0的自然数 针对以上四种情况,分别在add和remove写出对应的解法即可(但想写出可令人维护的直观代码并不容易) 这里可以很好的利用splice的原生方法来做对应匹配区间range的替换 下面给出一个自己的写法,只实现了add方法的第二种情况和remove方法的第一种情况
class RangeList {
  ranges = [
    [1, 5],
    [7, 10],
    [15, 89],
    [100, 105],
  ];

  constructor() {}

  _whichRange(num) {
    for (let index = 0; index < this.ranges.length; index++) {
      const [start, end] = this.ranges[index];
      if (num < start) {
        return [index - 1, index];
      }
      if (num >= start && num < end) {
        return index;
      }
    }
    return [this.ranges.length - 1];
  }

  _isInRange(judge) {
    return typeof judge === "number";
  }

  _isOutRange(judge) {
    if (Array.isArray(judge)) {
      return true;
    }
  }

  _replaceRange(range, start, len) {
    this.ranges.splice(start, len - start, range);
  }

  add(range) {
    if (!Array.isArray(range) || range.length !== 2) return;
    const [start, end] = range;
    const rangeStartIndex = this._whichRange(start);
    const rangeEndIndex = this._whichRange(end);

    if (this._isInRange(rangeStartIndex)) {
      // start命中range任意区间,end在区间断层,隔了N个区间
      if (this._isOutRange(rangeEndIndex)) {
        const _add = [this.ranges[rangeStartIndex][0], end];
        if (rangeEndIndex.length === 1) {
          this._replaceRange(
            _add,
            rangeStartIndex,
            this.ranges.length - rangeStartIndex
          );
        } else {
          const [_, _eIndex] = rangeEndIndex;
          this._replaceRange(
            _add,
            rangeStartIndex,
            _eIndex - rangeStartIndex
          );
        }
        return;
      }

      // start命中range任意区间,end区间内,隔了N个区间
      if (this._isInRange(rangeEndIndex)) {
        return;
      }
    }

    if (this._isOutRange(rangeStartIndex)) {
      // start在区间断层处,end在断层内,隔了N个区间
      if (this._isOutRange(rangeEndIndex)) {
        return;
      }

      // start在区间断层处,end区间内,隔了N个区间
      if (this._isInRange(rangeEndIndex)) {
        return;
      }
    }
  }

  remove(range) {
    if (!Array.isArray(range) || range.length !== 2) return;
    const [start, end] = range;
    const rangeStartIndex = this._whichRange(start);
    const rangeEndIndex = this._whichRange(end);

    if (this._isInRange(rangeStartIndex)) {
      if (this._isInRange(rangeEndIndex)) {
        const [rangeStart] = this.ranges[rangeStartIndex];
        const [_, rangeEnd] = this.ranges[rangeEndIndex];
        this.ranges.splice(
          rangeStartIndex,
          rangeEndIndex + 1,
          [rangeStart, start],
          [end, rangeEnd]
        );
        return;
      }
      if (this._isOutRange(rangeEndIndex)) {
        return;
      }
    }
  }

  print() {
    return this.ranges;
  }
}

const range = new RangeList();

range.add([4, 96]);
console.log(range.print()); // [ [ 1, 96 ], [ 100, 105 ] ]
range.remove([10, 56]);
console.log(range.print()); // [ [ 1, 10 ], [ 56, 96 ], [ 100, 105 ] ]
range.add([4, 106]);
console.log(range.print()); // [ [ 1, 106 ] ]