【算法】区间合并类题目总结


本文内容包括 重叠区间、不重叠区间、合并区间 的 左边界排序、右边界排序 方法。

重叠区间:452. 用最少数量的箭引爆气球

452. 用最少数量的箭引爆气球
在这里插入图片描述

解法1——左边界排序

按照左边界升序排序,在枚举的过程中只需要检查新来的左边界与旧的右边界的关系并更新最小右边界。

class Solution {
    public int findMinArrowShots(int[][] points) {
        Arrays.sort(points, (o1, o2) -> {
            return Integer.compare(o1[0], o2[0]);   //按左边界排序
        });
        int ans = 1, n = points.length, last = points[0][1];
        for (int i = 1; i < n; ++i) {
            if (points[i][0] > last) {      // 需要一个新的弓箭
                ++ans;
                last = points[i][1];
            } else last = Math.min(last, points[i][1]); // 更新最小右边界
        }
        return ans;
    }
}

解法2——右边界排序

按照右边界排序时,只需比较新来的左边界和当前右边界的关系。(由于是按右边界排序的,因此只有需要新弓箭时才需要更新右边界。)

class Solution {
    public int findMinArrowShots(int[][] points) {
        Arrays.sort(points, (o1, o2) -> {
            return Integer.compare(o1[1], o2[1]);
        });
        int ans = 1, n = points.length, last = points[0][1];
        for (int i = 1; i < n; ++i) {
            if (points[i][0] > last) {
                ++ans;
                last = points[i][1];
            }
        }
        return ans;
    }
}

无重叠区间:435. 无重叠区间

435. 无重叠区间
在这里插入图片描述

解法1——左边界排序

按左边界排序时,每次发现重叠的时候需要更新最小右边界,以此来保证每次发生重叠,保留的都是右边界靠左侧的。

class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {
        Arrays.sort(intervals, (a, b) -> {
            return a[0] - b[0];
        });
        int ans = 0, last = intervals[0][1];
        for (int i = 1; i < intervals.length; ++i) {
            if (intervals[i][0] < last) {
                ++ans;
                last = Math.min(intervals[i][1], last);
            } else last = intervals[i][1];
        }
        return ans;
    }
}

解法2——右边界排序

按照右边界排序时,只有不重叠时才需要更新右边界。(因为是按右边界排序的,因此每次保留的一定是最靠左的右边界了。)

class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {
        Arrays.sort(intervals, (a, b) -> {
            return Integer.compare(a[1], b[1]);
        });
        int ans = 0, last = intervals[0][1];
        for (int i = 1; i < intervals.length; ++i) {
            if (intervals[i][0] < last) ++ans;
            else last = intervals[i][1];
        }
        return ans;
    }
}

可以看出这道题和上一题超级像!

合并区间:56. 合并区间

56. 合并区间

在这里插入图片描述

左边界排序

class Solution {
    public int[][] merge(int[][] intervals) {
        Arrays.sort(intervals, (a, b) -> {
            return a[0] - b[0];
        });
        List<int[]> ans = new ArrayList();
        int last = Integer.MIN_VALUE;
        for (int i = 0; i < intervals.length; ++i) {
            if (intervals[i][0] > last) {
                ans.add(new int[]{intervals[i][0], intervals[i][1]});
                last = intervals[i][1];
            } else {
                last = Math.max(last, intervals[i][1]);
                ans.get(ans.size() - 1)[1] = last;
            }
        }
        return ans.toArray(new int[ans.size()][2]);
    }
}

这题为什么不能按照右边界排序?

看这样一个样例:[[2,3],[4,5],[6,7],[8,9],[1,10]]
按照右边界排序时,最后一个区间突然可以将之前的所有区间包裹住,但是前面的 4 个区间都各不重叠无法合并。

其实右边界排序也是可以写的!

其实右边界排序也是可以写的,把左边界排序的代码全都反过来操作就可以了!

class Solution {
    public int[][] merge(int[][] intervals) {
        Arrays.sort(intervals, (a, b) -> {
            return a[1] - b[1];
        });
        List<int[]> ans = new ArrayList();
        int last = Integer.MAX_VALUE;
        for (int i = intervals.length - 1; i >= 0; --i) {
            if (intervals[i][1] < last) {
                ans.add(new int[]{intervals[i][0], intervals[i][1]});
                last = intervals[i][0];
            } else {
                last = Math.min(last, intervals[i][0]);
                ans.get(ans.size() - 1)[0] = last;
            }
        }
        return ans.toArray(new int[ans.size()][2]);
    }
}

但其实没有必要对吧,本质上还是算同一种解法。

解法2——在原数组上合并再加入答案

class Solution {
public:
    //原数组上直接合并的写法
    static bool cmp(vector<int>&a,vector<int>&b){
        if(a[0]<b[0]) return true;                      //左边界升序排序
        return false;
    }
    vector<vector<int>>result;
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        sort(intervals.begin(), intervals.end(), cmp);    
        for(int i = 1; i < intervals.size(); i++){
            if(intervals[i][0] > intervals[i-1][1]){    //完全不重叠
                //把i-1放进去,而不是i
                result.push_back(intervals[i-1]);
            } else if(intervals[i][0] <= intervals[i-1][1]){
                //更新intervals[i]
                intervals[i][1] = max(intervals[i-1][1], intervals[i][1]);
                intervals[i][0] = min(intervals[i-1][0], intervals[i][0]);
            }
        }
        result.push_back(intervals[intervals.size() - 1]);
		return result;
    }
};

这种解法拿来练练思维还是挺好的,但还是建议重点学习解法1就好。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wei *

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值