我一直在寻找一种有效的算法来合并动态的间隔阵列上的重叠间隔。例如,(开始时间,结束时间)明智,
[(1, 2), (4, 8), (3, 10)]
成为
[(1, 2), (3, 10)]
因为(4,8)和(3,10)重叠。重叠意味着两个区间的任何部分共享相同的时刻。
我知道在给出完整数组时,可以在开始时间升序(reference: geeksforgeeks)对时间间隔进行排序后,使用堆栈完成操作。但是这个算法只有在给定的数组是非动态的时候才有效,但是我正在搜索哪个对动态数组有效。例如,数组间隔将被频繁更新和插入,并且每个操作都应合并间隔。
答案 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