区间覆盖与合并

问题

最近打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;
}
阅读更多
个人分类: 算法 leetcode
想对作者说点什么? 我来说一句

C语言多区间合并的简单实现

2014年05月20日 4KB 下载

没有更多推荐了,返回首页

不良信息举报

区间覆盖与合并

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭