区间覆盖与合并

原创 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-面试算法经典-Java实现】【056-Merge Intervals(区间合并)】

【056-Merge Intervals(区间合并)】【LeetCode-面试算法经典-Java实现】【所有题目目录索引】原题  Given a collection of intervals, me...

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

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

LintCode- 合并区间

合并区间 给出若干闭合区间,合并所有重叠的部分。样例给出的区间列表 => 合并后的区间列表:[ [ [1, 3], [1, 6...

区间合并算法

需求:给定多个连续的区间,将区间进行合并,输出一个或多个没有交集的连续区间。 支持泛型,可以用数值或者字符串表示区间(实际上是Comparable接口)。 环境:jdk1.8 package c...

合并区间

给出若干闭合区间,合并所有重叠的部分。 样例 给出的区间列表 => 合并后的区间列表: [ [ [1, 3], [...
  • brucehb
  • brucehb
  • 2015年08月12日 01:01
  • 2386

区间重合判定

区间重合判定

区间合并

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

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

分治-区间合并 7620

给定 n 个闭区间 [ai; bi],其中i=1,2,…,n。任意两个相邻或相交的闭区间可以合并为一个闭区间。例如,[1;2] 和 [2;3] 可以合并为 [1;3],[1;3] 和 [2;4] 可以...

mapreduce实现对key的排序

http://blog.csdn.net/evo_steven/article/details/14521713 最近在学习MapReduce编程遇到很多用MR实现按某一列值排序,或二次排序的类...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:区间覆盖与合并
举报原因:
原因补充:

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