Interval range insert into intervals without merging existing intervals

时间:2019-01-09 22:30:42

标签: javascript algorithm

Problem description: The idea is to insert into existing intervals new interval which doesn't merge with existing intervals but fills the missing gaps between intervals. (This is not the interval merging problem)

For example, inserting interval [0, 7] to intervals [[0, 1], [3, 5]] would result new intervals with gaps filled [[0, 1], [1, 3], [3, 5], [5, 7]].

Interval range is already sorted smallest to larger [[0, 1], [3, 5]].

My current solution is a bit "broken", I ended up using too many if checks to cover some special cases which makes everything more complex then needed. I am looking for better ways to simplify the condition part. In the bottom of the code there are test cases included, also cases where my solution fails.

The test cases where my algorithm is failing and producing wrong results:

    assert.deepEqual( // Broken
      insertIntervalSec([[1,5], [7,10]], [4, 12]),
      [[1,5], [5, 7], [7,10], [10, 12]],
    );
    assert.deepEqual(insertIntervalSec([[1,1]], [1,3]), [[1,3]]); // Broken
function isOverLapping(a, b) {
  return Math.max(a[0], b[0]) <= Math.min(a[1], b[1]);
}

function insertIntervalSec(arr, interval) {
  const result = [];
  let i = 0;

  const contains = (a, b) => {
    return a[0] >= b[0] && a[1] <= b[1]
  };

  if (arr.length <= 0) {
    result.push(interval);
    return result;
  }
  if (arr.length === 1 && contains(interval, arr[0])) {
    result.push(interval);
    return result;
  }

  // Start point
  if (interval[1] >= arr[0][0] && isOverLapping(interval, arr[0])) {
    result.push([interval[0], arr[0][0]]);
  } else if (interval[1] <= arr[0][0]) {
    result.push([interval[0], Math.min(interval[1], arr[0][0])]);
  }

  while (i < arr.length) {
    const current = arr[i];
    result.push(arr[i]);

    if (!contains(interval, arr[i]) && isOverLapping(arr[i], interval)) {
      const next = arr[i + 1];

      // Special handling for the last item
      if (next !== undefined) {
        if (interval[1] > current[1]) {
          result.push([current[1], next[0]]);
        }
      } else {
        if (interval[0] <= current[0] && interval[1] <= current[1]) {
          // TODO: No action
        } else if (interval[0] >= current[0] || interval[1] >= current[0]) {
          result.push([current[1], interval[1]]);
        }
      }
    }
    i++;
  }

  // End point
  const len = arr.length;
  const last = arr[len - 1];
  if (last[1] <= interval[0] && !isOverLapping(last, interval)) {
    result.push(interval);
  }

  return result;
}

assert.deepEqual(
  insertIntervalSec([[1,5],[10,15],[20,25]], [12,27]),
  [[1,5],[10,15],[15,20],[20,25],[25, 27]]
);

assert.deepEqual(
  insertIntervalSec([[1,5],[10,15],[20,25]], [-3,0]),
  [[-3,0],[1,5],[10,15],[20,25]]
);

assert.deepEqual(
  insertIntervalSec([[1,5],[10,15],[20,25]], [-3,3]),
  [[-3,1],[1,5],[10,15],[20,25]]
);

assert.deepEqual(
  insertIntervalSec([[0,5],[10,15],[20,25]], [15,15]),
  [[0,5],[10,15],[20,25]]
);
assert.deepEqual(
  insertIntervalSec([[0,5],[10,15],[20,25]], [20,21]),
  [[0,5],[10,15],[20,25]]
);
assert.deepEqual(
  insertIntervalSec([[0,5],[10,15],[20,25]], [26,27]),
  [[0,5],[10,15],[20,25],[26, 27]]
);
assert.deepEqual(
  insertIntervalSec([[0,5],[10,15],[20,25]], [25,27]),
  [[0,5],[10,15],[20,25],[25,27]]
);
assert.deepEqual(insertIntervalSec([], [25,27]), [[25,27]]);
assert.deepEqual(insertIntervalSec([[1,1]], [1,1]), [[1,1]]);
assert.deepEqual( // Broken
  insertIntervalSec([[1,5], [7,10]], [4, 12]),
  [[1,5], [5, 7], [7,10], [10, 12]],
);
assert.deepEqual(insertIntervalSec([[1,1]], [1,3]), [[1,3]]); // Broken

assert.deepEqual(
  insertIntervalSec2([[5,5]], [6,6]),
  [[5,5], [6,6]]
);

assert.deepEqual(
  insertIntervalSec2([[1,3]], [6,6]),
  [[1,3], [6,6]]
);

1 个答案:

答案 0 :(得分:2)

除了最后一个测试用例(请参阅问题注释)外,它通过了所有测试。基本思想是只跟踪start变量,该变量指示已使用多少插入范围。这使您可以将其缩小为三种情况:

  1. 插入的间隔完全适合当前项目的前面
  2. 迭代中的当前项目在插入间隔之前完全适合
  3. 迭代中的项目重叠。

迭代这些项目之后,您可以检查插入的范围是否还有要插入的内容:

function insertIntervalSec(arr, insert) {
  let start = insert[0]
  let res = []
  for (i = 0; i < arr.length; i++) {
    let a = arr[i]
    // smaller item in range
    if (a[0] <= start) {
      res.push(a)
      start = Math.max(a[1], start)
      continue
    }
    // moved past inserted interval add rest of arr
    if (start >= insert[1]) {
      res.push(...arr.splice(i))
      break
    }

    // fill in spaces
    let end = Math.min(insert[1], a[0])
    res.push([start, end], a)
    start = a[1]
  }
  // clean up left over range
  if (start < insert[1]) res.push([start, insert[1]])

  return res
}

console.log(insertIntervalSec([ [1, 5],[10, 15],[20, 25]], [-2, 27]))