关闭

区间覆盖与合并

721人阅读 评论(0) 收藏 举报
分类:

问题

最近打google的apactest,遇到一个经典的(但我不熟的)问题——给你一堆整数区间(比如[1, 3], [2, 6], [8, 10]),问它们合并后是怎样的?
比如上述三个区间合并后就变成:[1, 6], [8, 10]。

这个问题在leetcode上的难度评级是Hard,简直亮瞎啊,其实并不难呀:https://leetcode.com/problems/merge-intervals/

思路1

如果,区间的端点的范围很小,比如都在[0, 9999],那很简单地开个bool数组,然后对于每个区间覆盖到的点都赋值为true,最后的区间就是那些连续一片一片的true,比如上述的三个区间的话,bool数组将是这样的:0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0,…把这些连续的true的端点提取出来就是合并后的区间啦!

这样做的限制是:
1. 区间的范围得较小,不然没法开数组;
2. 区间的平均跨度不是很大或区间的数量不多。

为什么会有2这条限制呢?先算一下这样做的复杂度:
假设区间的平均跨度为L,有N个区间,所有区间端点的范围的长度为M,那么时间复杂度就是O(L*N + M)。如果L很大并且N很大,跟M一个数量级的话,那么这个算法将会超级慢的!

思路2

想法是很容易出来的,我们先按区间的左端点排序(不要问为什么,因为直觉,不排序的话,基本没法做啊)。

然后维护一个left和right值,表示当前已有的连续区间的左端点和右端点,它们的初始值自然就是第一个区间的端点值啦,还是拿上面的那三个区间作为例子。

第一步:[1, 3],则left = 1, right = 3
第二步:[2, 6],发现2还是小于right的,说明当前这个区间和前面的还是“连在一起的”,所以left值不用变,只需要更新right值就好了,right明显等于max(3, 6) = 6。
第三步,[8, 10],发现8比right大,所以当前这个区间和前面的区间是“断开”的,前面的区间独立作为一个,然后left和right都从这个区间开始,嗯。

最后的结果自然是:[1, 6],[8, 10]啦~

分析一下时间复杂度,排序的时候是O(NlogN),然后线性扫一遍是O(N),所以总的时间复杂度是O(NlogN),跟区间的跨度没关系,跟区间端点的覆盖范围也没一毛钱关系!
只跟区间的个数有关系,而且还是很可以接受的一个复杂度(毕竟log N跟常数的区别不是很大)。

代码

只给出第二种思路的代码,第一种太过简单,不用了吧:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

typedef vector<pair<int, int> > RangeList;

RangeList cover(const RangeList& intervals) {
    int left = intervals[0].first, right = intervals[0].second;
    RangeList result;

    for (int i = 1; i < intervals.size(); ++i) {
        // 前面自成一个区间,那么就此分开
        if (intervals[i].first > right) {
            result.push_back(make_pair(left, right));
            left = intervals[i].first;
            right = intervals[i].second;
        } else if (intervals[i].second > right) {
            right = intervals[i].second;
        }
    }
    result.push_back(make_pair(left, right));

    return result;
}

void display(const RangeList& intervals) {
    for (int i = 0; i < intervals.size(); ++i)
        cout << intervals[i].first << ' ' << intervals[i].second << endl;
    cout << endl;
}

int main() {
    RangeList intervals;
    int n, start, end;
    cin >> n;
    for (int i = 0; i < n; ++i) {
        cin >> start >> end;
        intervals.push_back(make_pair(start, end));
    }
    sort(intervals.begin(), intervals.end());

    display(intervals);
    RangeList result = cover(intervals);
    display(result);

    return 0;
}
1
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:129075次
    • 积分:2696
    • 等级:
    • 排名:第13898名
    • 原创:142篇
    • 转载:21篇
    • 译文:1篇
    • 评论:26条
    最新评论