题解 | #合并区间#

合并区间

http://www.nowcoder.com/practice/69f4e5b7ad284a478777cb2a17fb5e6a

题目主要信息:
  • 给出一组区间,区间包括起始点,要求将重叠的区间合并
  • 重叠后的区间按照起点位置升序排列
举一反三:

学习完本题的思路你可以解决如下题目:

BM95. 分糖果问题

BM96. 主持人调度

方法: 排序+贪心(推荐使用)

知识点:贪心思想

贪心思想属于动态规划思想中的一种,其基本原理是找出整体当中给的每个局部子结构的最优解,并且最终将所有的这些局部最优解结合起来形成整体上的一个最优解。

思路:

什么样的区间能够合并,那肯定是有交叉的区间,即后一个区间的尾小于前一个区间的首,这时候可以将这种交叉区间的尾合并延长区间:

//区间有重叠,更新结尾
if(intervals[i].start <= res.back().end) 
    res.back().end = max(res.back().end, intervals[i].end);

那我们肯定是区间在一定程度上有序的才可以方便比较(区间有两个边界值,完全有序不可能,但是可以按照区间首排序),这时候只要遍历到交叉的情况,就利用贪心思想,一直合并,直到不能合并为止。

具体做法:

  • step 1:既然要求重叠后的区间按照起点位置升序排列,我们就将所有区间按照起点位置先进行排序。使用sort函数进行排序,重载比较方式为比较interval结构的start变量。
  • step 2:排序后的第一个区间一定是起点值最小的区间,我们将其计入返回数组res,然后遍历后续区间。
  • step 3:后续遍历过程中,如果遇到起点值小于res中最后一个区间的末尾值的情况,那一定是重叠,取二者最大末尾值更新res中最后一个区间即可。
  • step 4:如果遇到起点值大于res中最后一个区间的末尾值的情况,那一定没有重叠,后续也不会有这个末尾的重叠区间了,因为后面的起点只会更大,因此可以将它加入res。

图示:

alt

Java代码实现:

import java.util.*;
public class Solution {
    public ArrayList<Interval> merge(ArrayList<Interval> intervals) {
        ArrayList<Interval> res = new ArrayList<>();
        //去除特殊情况
        if(intervals.size() == 0) 
            return res;
        //重载比较,按照区间首排序
        Collections.sort(intervals, new Comparator<Interval>(){
            public int compare(Interval o1, Interval o2){
                if(o1.start != o2.start)
                    return o1.start - o2.start;
                else
                    return o1.end - o2.end;
            }
        }); 
        //放入第一个区间
        res.add(intervals.get(0)); 
        int count = 0;
        //遍历后续区间,查看是否与末尾有重叠
        for(int i = 1; i < intervals.size(); i++){
            Interval o1 = intervals.get(i);
            Interval origin = res.get(count);
            if(o1.start > origin.end){
                res.add(o1);
                count++;
            //区间有重叠,更新结尾
            }else{ 
                res.remove(count);
                Interval s = new Interval(origin.start, o1.end);
                if(o1.end < origin.end)
                    s.end = origin.end;
                res.add(s);
            }
        }
        return res;
    }
}

C++代码实现:

class Solution {
public:
    //重载比较
    static bool cmp(Interval &a, Interval &b) { 
        return a.start < b.start;
    }
    
    vector<Interval> merge(vector<Interval> &intervals) {
        vector<Interval> res;
        //去除特殊情况
        if(intervals.size() == 0) 
            return res;
        //按照区间首排序
        sort(intervals.begin(), intervals.end(), cmp); 
        //放入第一个区间
        res.push_back(intervals[0]); 
        //遍历后续区间,查看是否与末尾有重叠
        for(int i = 1; i < intervals.size(); i++){ 
            //区间有重叠,更新结尾
            if(intervals[i].start <= res.back().end) 
                res.back().end = max(res.back().end, intervals[i].end);
            //区间没有重叠,直接加入
            else 
                res.push_back(intervals[i]);
        }
        return res;
    }
};

Python实现代码:

from functools import cmp_to_key

class Solution:
    def merge(self , intervals: List[Interval]) -> List[Interval]:
        res = list()
        #去除特殊情况
        if len(intervals) == 0: 
            return res
        #按照区间首排序
        intervals.sort(key=cmp_to_key(lambda a,b:a.start - b.start))
        #放入第一个区间
        res.append(intervals[0]) 
        #遍历后续区间,查看是否与末尾有重叠
        for i in range(len(intervals)): 
            #区间有重叠,更新结尾
            if intervals[i].start <= res[-1].end: 
                res[-1].end = max(res[-1].end, intervals[i].end)
            #区间没有重叠,直接加入
            else: 
                res.append(intervals[i])
        return res


复杂度分析:

  • 时间复杂度:O(nlog2n)O(nlog_2n),排序的复杂度为O(nlog2n)O(nlog_2n),后续遍历所有区间的复杂度为O(n)O(n),属于低次幂,忽略
  • 空间复杂度:O(1)O(1),res为返回必要空间,没有使用额外辅助空间
全部评论
“什么样的区间能够合并,那肯定是有交叉的区间,即后一个区间的尾小于前一个区间的首” 官方的这句话我理解不了,不应该是后一区间的首小于前一区间的尾吗?
8 回复 分享
发布于 2022-07-16 15:28
应该是后一个区间的首小于前一个区间的尾
4 回复 分享
发布于 2022-11-09 14:40 新疆
进阶版时间复杂度O(N)的解法呢
2 回复 分享
发布于 2022-05-01 11:16
python 解法里的lambda a,b:a.start - b.start,这行代码啥意思
2 回复 分享
发布于 2023-08-22 16:14 广东
这里用前缀和可以达到线性时间复杂度
1 回复 分享
发布于 2023-02-07 20:52 北京
python版本内存超过限制..
点赞 回复 分享
发布于 2022-07-31 17:03
python版本里,一个数组元素的.start和.end是什么写法呀?
点赞 回复 分享
发布于 2023-01-04 19:27 北京
我寻思跟我差不多啊,为啥我就超时 class Solution: def merge(self , intervals: List[Interval]) -> List[Interval]: # write code here intervals.sort(key=lambda x: x.start) i = 0 while i <= len(intervals)-2: if intervals[i].end >= intervals[i+1].start: if intervals[i].end < intervals[i+1].end: intervals[i].end = intervals[i+1].end intervals.pop(i+1) else: intervals.pop(i+1) else: i += 1 return intervals
点赞 回复 分享
发布于 2023-04-07 23:34 陕西
def merge(self , intervals: List[Interval]) -> List[Interval]:为什么在本地这样写会报错NameError: name 'Interval' is not defined
点赞 回复 分享
发布于 2023-09-11 22:20 广东
我寻思也差不多啊
点赞 回复 分享
发布于 2023-11-22 21:27 河北
你这后一个区间的尾小于前一个区间的首???不应该是后一个区间的首小于前一个区间的尾吗?
点赞 回复 分享
发布于 05-19 09:12 河北
Python第16行会报:AttributeError: 'list' object has no attribute 'end'
点赞 回复 分享
发布于 09-02 18:55 广东

相关推荐

11-09 14:54
已编辑
华南农业大学 产品经理
大拿老师:这个简历,连手机号码和照片都没打码,那为什么关键要素求职职位就不写呢? 从上往下看,都没看出自己到底是产品经理的简历,还是电子硬件的简历? 这是一个大问题,当然,更大的问题是实习经历的描述是不对的 不要只是去写实习流程,陈平,怎么去开会?怎么去讨论? 面试问的是你的产品功能点,是怎么设计的?也就是要写项目的亮点,有什么功能?这个功能有什么难处?怎么去解决的? 实习流程大家都一样,没什么优势,也没有提问点,没有提问,你就不得分 另外,你要明确你投的是什么职位,如果投的是产品职位,你的项目经历写的全都是跟产品无关的,那你的简历就没用 你的面试官必然是一个资深的产品经理,他不会去问那些计算机类的编程项目 所以这种四不像的简历,在校招是大忌
点赞 评论 收藏
分享
53 17 评论
分享
牛客网
牛客企业服务