合并重叠区间的算法

时间:2017-06-20 18:04:03

标签: arrays algorithm dynamic merge intervals

我一直在寻找一种有效的算法来合并动态的间隔阵列上的重叠间隔。例如,(开始时间,结束时间)明智,

[(1, 2), (4, 8), (3, 10)]

成为

[(1, 2), (3, 10)]

合并后

因为(4,8)和(3,10)重叠。重叠意味着两个区间的任何部分共享相同的时刻。

我知道在给出完整数组时,可以在开始时间升序(reference: geeksforgeeks)对时间间隔进行排序后,使用堆栈完成操作。但是这个算法只有在给定的数组是非动态的时候才有效,但是我正在搜索哪个对动态数组有效。例如,数组间隔将被频繁更新和插入,并且每个操作都应合并间隔。

1 个答案:

答案 0 :(得分:3)

保留一个间隔的二叉搜索树(BST),其中键是间隔的起始点。

对于要插入的任何新间隔:

  • 找到BST中的最大值小于新间隔的起点(可以在O(log n)中完成)。

    该间隔或下一个间隔将与新间隔重叠,或者没有重叠(在这种情况下,我们只是插入)。

  • 可能有更多的间隔与新间隔重叠,因此从这里我们需要迭代BST的其余部分(从上面找到的点开始)并将间隔与任何重叠间隔合并。

虽然在最坏的情况下任何给定的插入都可以取O(n log n)(如果间隔与例如每隔一个间隔重叠),则每次插入的摊销时间将为O(log n)(因为我们可以计算完成删除元素的工作,作为插入它的工作的一部分,总共是O(log n)工作。

一些经过轻度测试的C ++代码执行此操作:

// set<pair<int, int> > can also be used, but this way seems conceptually simpler
map<int, pair<int, int> > intervals;

void insert(int left, int right)
{
  if (!intervals.empty())
  {
    // get the next bigger element
    auto current = intervals.upper_bound(left);
    // checking if not found is not needed because of decrement and how C++ iterators work
    // decrement to get next smaller one instead, but only if we're not that the beginning
    if (current != intervals.begin())
        --current;
    // skip current if it's entirely to the left of the new interval
    if (current->second.second < left)
        ++current;
    // update new starting point if there's an overlap and current is more to the left
    if (current != intervals.end() && current->first <= right)
        left = min(left, current->first);
    // iterate through while there's an overlap (deleting as we go)
    for (; current != intervals.end() && current->first <= right;
           current = intervals.erase(current))
        // update the end point of new interval
        right = max(right, current->second.second);
  }
  // insert our updated interval
  intervals[left] = make_pair(left, right);
}

// FYI: current->first is the start, current->second.second is the end

Live demo