区间覆盖与合并

原创 2016年08月30日 11:31:29

问题

最近打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区间问题

区间问题在笔试过程中会问,这里总结如下: 1 57. Insert Interval Given a set of non-overlapping intervals, insert a ...
  • yanerhao
  • yanerhao
  • 2017年08月23日 20:49
  • 259

区间合并

总时间限制: 1000ms 内存限制: 65536kB 描述 给定 n 个闭区间 [ai; bi],其中i=1,2,...,n。任意两个相邻或相交的闭区间可以合并为一个闭区间。例如,[1;2] 和...
  • Wanglinlin_bfcx
  • Wanglinlin_bfcx
  • 2017年02月16日 20:34
  • 1013

分治-区间合并 7620

给定 n 个闭区间 [ai; bi],其中i=1,2,…,n。任意两个相邻或相交的闭区间可以合并为一个闭区间。例如,[1;2] 和 [2;3] 可以合并为 [1;3],[1;3] 和 [2;4] 可以...
  • qq_26140973
  • qq_26140973
  • 2016年12月04日 12:47
  • 746

求多个区间合并后区间大小的巧妙解决方法

上图一共有5个区间,分别是[0,2]、[2,4]、[8,11]、[7,11]、[15,18]。如果要求这些区间合并后区间的大小,有两种简单的方法。   方法一:比较每两个区间的范围,如果两个区间有交...
  • RaceBug2010
  • RaceBug2010
  • 2012年07月02日 01:40
  • 6816

sdnuACM1012区间合并问题

Description 给定n个开区间,合并他们中的重合者,输出合并后的区间数量。 Input 第一行:n(1 第2至第n+1行:每行两个整数,第i行的两个整数表示第...
  • Suyebiubiu
  • Suyebiubiu
  • 2016年10月24日 13:15
  • 525

C++ 【对若干个区间进行合并】

最近遇到测试结果区域重叠,如test1=0005.png ,C,1  200, 157  345,520  700. 第一区间和第二区间部分重 叠。  参考博客:http://blog.csdn.ne...
  • zhangdongdong565
  • zhangdongdong565
  • 2017年09月03日 20:51
  • 260

合并区间-lintcode

步骤: 1.先对vector排序。 2.再按要求合并。 用到的知识:sort存在于algorithm头文件中; vector的erase()的参数是迭代器。 C++代码: /** * De...
  • u014257954
  • u014257954
  • 2016年12月17日 10:52
  • 176

LintCode_合并区间

笔试面试算法题
  • Tina_yaoyao
  • Tina_yaoyao
  • 2015年10月30日 10:50
  • 635

相邻区间合并

  • 2013年04月01日 23:55
  • 799B
  • 下载

lintcode-156-合并区间

给出若干闭合区间,合并所有重叠的部分。 样例 给出的区间列表 => 合并后的区间列表: [ [ [1, 3], [...
  • ljlstart
  • ljlstart
  • 2015年09月05日 00:56
  • 1206
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:区间覆盖与合并
举报原因:
原因补充:

(最多只允许输入30个字)