🔥博客介绍`: 27dCnc
🎥系列专栏: <<数据结构与算法>> << 算法入门>> << C++项目>>
🎥 当前专栏: <<数据结构与算法>>
专题 : 数据结构帮助小白快速入门算法
👍👍👍👍👍👍👍👍👍👍👍👍
☆*: .。. o(≧▽≦)o .。.:*☆
❤️感谢大家点赞👍收藏⭐评论✍️
学习目标:
今日学习打卡
- 代码随想录-贪心算法
学习时间:
- 周一至周五晚上 7 点—晚上9点
- 周六上午 9 点-上午 11 点
- 周日下午 3 点-下午 6 点
学习内容:
- 无重叠区间
- 划分字母区间
- 合并区间
- 单调递增的数字
内容详细:
无重叠区间
题目考点: 贪心
区间
解题思路
排序,找重叠区间,然后标记排除
我来按照右边界排序,从左向右记录非交叉区间的个数。最后用区间总数减去非交叉区间的个数就是需要移除的区间个数了。
此时问题就是要求非交叉区间的最大个数。
这里记录非交叉区间的个数还是有技巧的,如图:
class Solution {
public:
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
sort(intervals.begin(),intervals.end(),[](const vector<int>&a,const vector<int>&b) {
return a[0] < b[0];
});
if (intervals.size() == 0) return 0;
int cnt = 0;
int end = intervals[0][1];
for(int i = 1; i < intervals.size(); i++) {
if (intervals[i][0] >= end) end = intervals[i][1]; //无重叠情况
else {
end = min(end,intervals[i][1]);
cnt++; //记录重叠情况
}
}
return cnt;
}
};
划分字母区间
题目考点: 贪心
在遍历的过程中相当于是要找每一个字母的边界,如果找到之前遍历过的所有字母的最远边界,说明这个边界就是分割点了。此时前面出现过所有字母,最远也就到这个边界了。
可以分为如下两步:
- 统计每一个字符最后出现的位置
- 从头遍历字符,并更新字符的最远出现下标,如果找到字符最远出现位置下标和当前下标相等了,则找到了分割点
代码
class Solution {
public:
vector<int> partitionLabels(string S) {
int hash[27] = {0}; // i为字符,hash[i]为字符出现的最后位置
for (int i = 0; i < S.size(); i++) { // 统计每一个字符最后出现的位置
hash[S[i] - 'a'] = i;
}
vector<int> ans;
int left = 0;
int right = 0;
for (int i = 0; i < S.size(); i++) {
right = max(right, hash[S[i] - 'a']); // 找到字符出现的最远边界
if (i == right) {
ans.push_back(right - left + 1);
left = i + 1;
}
}
return ans;
}
};
合并区间
题目考点: 区间
贪心
题解
对区级左右端点的一个端点进行排序, 然后查找重叠区间
这几道题都是判断区间重叠,区别就是判断区间重叠后的逻辑,本题是判断区间重贴后要进行区间合并。
所以一样的套路,先排序,让所有的相邻区间尽可能的重叠在一起,按左边界,或者右边界排序都可以,处理逻辑稍有不同。
按照左边界从小到大排序之后,如果 intervals[i][0] <= intervals[i - 1][1] 即intervals[i]的左边界 <= intervals[i - 1]的右边界,则一定有重叠。(本题相邻区间也算重贴,所以是<=)
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
vector<vector<int>> ans;
sort(intervals.begin(),intervals.end(),[](const vector<int>&a,const vector<int>&b) {
return a[0] < b[0];
});
for (int i = 1; i < intervals.size(); i++) {
if (intervals[i][0] <= intervals[i-1][1]) {
intervals[i][1] = max(intervals[i][1], intervals[i-1][1]);
intervals[i][0] = intervals[i-1][0];
} else {
ans.push_back({intervals[i-1][0],intervals[i-1][1]});
}
}
ans.push_back({intervals.back()[0], intervals.back()[1]});
return ans;
}
};
单调递增的数字
题目考点: 贪心
数据
解题思路
题目要求小于等于N的最大单调递增的整数,那么拿一个两位的数字来举例。
例如:98,一旦出现strNum[i - 1] > strNum[i]的情况(非单调递增),首先想让strNum[i - 1]–,然后strNum[i]给为9,这样这个整数就是89,即小于98的最大的单调递增整数。
这一点如果想清楚了,这道题就好办了。
此时是从前向后遍历还是从后向前遍历呢?
从前向后遍历的话,遇到strNum[i - 1] > strNum[i]的情况,让strNum[i - 1]减一,但此时如果strNum[i - 1]减一了,可能又小于strNum[i - 2]。
这么说有点抽象,举个例子,数字:332,从前向后遍历的话,那么就把变成了329,此时2又小于了第一位的3了,真正的结果应该是299。
那么从后向前遍历,就可以重复利用上次比较得出的结果了,从后向前遍历332的数值变化为:332 -> 329 -> 299
确定了遍历顺序之后,那么此时局部最优就可以推出全局,找不出反例,试试贪心。
代码
class Solution {
public:
int monotoneIncreasingDigits(int N) {
string strNum = to_string(N);
// flag用来标记赋值9从哪里开始
// 设置为这个默认值,为了防止第二个for循环在flag没有被赋值的情况下执行
int flag = strNum.size();
for (int i = strNum.size() - 1; i > 0; i--) {
if (strNum[i - 1] > strNum[i] ) {
flag = i;
strNum[i - 1]--;
}
}
for (int i = flag; i < strNum.size(); i++) {
strNum[i] = '9';
}
return stoi(strNum);
}
};
学习产出:
- 技术笔记 2 遍
- CSDN 技术博客 3 篇
- 习的 vlog 视频 1 个
重磅消息:
GTP - 4 最新版接入服务他来了 点击链接即可查看详细
GTP - 4 搭建教程
🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏、✍️评论,支持一下博主~