【算法】【贪心】贪心算法解决两道经典区间问题 【力扣-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数组是无序的,如果气球排列的横坐标是有序的,我们很轻易就可以看出气球之间是否有重叠了
局部最优:重叠时,一起射爆所用箭最少
全局最优:射爆所有气球所用弓箭最少
因此,我们的算法大致思路为:
- 按照气球的起始位置排序(还是终止位置?都可以)
- 遍历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;
}
};
尾声
看到这里,相信伙伴们对这两道经典的区间问题已经有了一定理解了。。
最后,如果你感觉在这篇文章里学到东西的话,千千万万不要忘了点赞收藏关注后再离开哦!