【算法】【贪心】贪心算法解决两道经典区间问题 【力扣-452,力扣-56】超详细的解释和注释

【算法】【贪心】贪心算法解决两道经典区间问题 【力扣-452,力扣-56】超详细的解释和注释

先赞后看好习惯 打字不容易,这都是很用心做的,希望得到支持你 大家的点赞和支持对于我来说是一种非常重要的动力 看完之后别忘记关注我哦!️️️

区间问题相信大家已经很熟悉了,各种去重覆盖的问题,我们今天介绍两道题,利用贪心算法解决这两道区间问题。今天要介绍的两道题分别是

【leetcode-452 用最少的箭射爆气球】和
【leetcode-56 合并区间】 

上一期我介绍了贪心算法解决两道股票问题的方法,需要的伙伴可以通过传送门食用~【算法】【贪心】贪心算法解决两道基础股票买卖问题-【力扣-122,力扣-714】贪心算法基础入门题目超详细讲解

这期的内容干货满满,建议收藏后食用~

OJ-452 用最少数量的箭射爆气球

题目描述

算法思路和代码实现

首先,我们想,如果需要用最少的箭射爆最多的气球,很明显,最好的情况就是1支箭射爆多个气球,越多越好。因此,我们可以得出一个结论,如果气球的坐标有重叠区间,我们用一只箭就可以射爆这些气球。
我们就用Leetcode题目的举例来举两个例子
例子1:若
points = [[1,2],[3,4],[5,6],[7,8]]

此时,我们的答案肯定是4,因为4个气球完全没有重叠空间,每个气球都需要用一支箭来处理.
例子2:若
points = [[10,16],[2,8],[1,6],[7,12]]

此时答案是2,一支箭处理中间两个,一支箭处理外面两个
但是,这个例子相对于上面那个例子反而没那么明显,这是因为,我们此时给的points数组是无序的,如果气球排列的横坐标是有序的,我们很轻易就可以看出气球之间是否有重叠了
局部最优:重叠时,一起射爆所用箭最少
全局最优:射爆所有气球所用弓箭最少

因此,我们的算法大致思路为:

  1. 按照气球的起始位置排序(还是终止位置?都可以)
  2. 遍历points观察重叠情况即可

代码为:

#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;
class Solution {
private:
    static bool cmp(const vector<int>& a, const vector<int>& b) {
        return a[0] < b[0];
    }
public:
    int findMinArrowShots(vector<vector<int>>& points) {
        //[[10,16],[2,8],[1,6],[7,12]]
        //[[1,6],[2,8],[7,12],[10,16]]
        if (points.size() == 0)//没有元素,一支箭也不用
            return 0;
        //排序
        sort(points.begin(), points.end(), cmp);
        
        int result = 1;//points不为空,所以肯定至少要一支箭
        for (int i = 1; i < points.size(); i++) {
            //从第二个气球开始看,看能不能射爆同时把前面那个搞定
            if (points[i][0] > points[i - 1][1]) {
            //如果此个气球的起始坐标大于前一个气球的终止坐标,无重叠,需多一支箭
                result++;
            }
            else {
            //有重叠
                //气球i和气球i-1贴着
                //更新重叠气球的最小右边边界
                points[i][1] = min(points[i - 1][1], points[i][1]);
                //这里为什么是min()呢,因为如果我们想要一次性射爆,射的位置是交集的位置,不是并集的位置
            }
        }
        return result;
    }
};

OJ-56 合并区间

题目描述

在这里插入图片描述

算法思路和代码实现

此题和上题一样,要处理重叠的区间,因此,我们肯定是要排序
按照左边界排序和右边界排序都是可以的
排序过后我们就可以很清晰地处理重叠空间了
那么这题和上一题的区别在于,我们合并完一次之后,取的应该是最大的右边界,而不是较小那个
因为上一题我们是一定要区间有交集才能一起射爆,但是此题不同,我们取最大右区间,可以最大程度地合并区间,这个就是本题贪心中的局部最优

代码实现:

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

class Solution {
private:
    static bool cmp(const vector<int>& a, const vector<int>& b) {
        return a[0] < b[0];
    }
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        //先创建一个结果二维vector
        vector<vector<int>> result;
        if (intervals.size() == 0) {//如果没有元素传进来
            return result;//直接返回空的即可
        }

        //排序
        sort(intervals.begin(), intervals.end(), cmp);
        //tips:排序时的参数也可以食用Lambda表达式

        //先把第一个插进来先
        result.push_back(intervals[0]);
        //开始遍历,从第二个元素,即下标为1的位置开始遍历
        for (int i = 1; i < intervals.size(); i++) {
            if (result.back()[1] >= intervals[i][0]) {//结果容器最后到达的位置大于准备插入元素的位置时,合并!
            //注意!是>=即可
                                                     //tips:元素指的是一个区间
                result.back()[1] = max(result.back()[1], intervals[i][1]);//更新result容器里面最后到达的位置
                                     ///两者之间挑大的那个,因为合并了
            }
            else {
                result.push_back(intervals[i]);//不能合并 直接插进来
            }
        }
        return result;
    }
};

尾声

看到这里,相信伙伴们对这两道经典的区间问题已经有了一定理解了。。
最后,如果你感觉在这篇文章里学到东西的话,千千万万不要忘了点赞收藏关注后再离开哦!

posted @ 2022-02-06 19:57  背包Yu  阅读(4)  评论(0编辑  收藏  举报  来源