今天开始贪心算法,贪心算法没什么套路和规律可循,只要局部最优推出全局最优就可以了。
每到贪心之间都是没有关联的,一个题一个样,贪心就是东一撇子西一扫帚的。
贪心算法要么很简单,简单到你会怀疑这是贪心吗,要么就贼难,难到你怀疑人生,所以不要小看贪心算法哦。
题目链接:455. 分发饼干 - 力扣(LeetCode)
视频链接:贪心算法,你想先喂哪个小孩?| LeetCode:455.分发饼干_哔哩哔哩_bilibili
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i
,都有一个胃口值 g[i]
,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j
,都有一个尺寸 s[j]
。如果 s[j] >= g[i]
,我们可以将这个饼干 j
分配给孩子 i
,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
示例 1:
输入: g = [1,2,3], s = [1,1] 输出: 1 解释: 你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。 虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。 所以你应该输出1。
在此之前,需要将小孩胃口和饼干排个序!
本题的思路有两种
第一种(小饼干满足小胃口):刚开始我想到的就是这种方法,先用小饼干满足小胃口的孩子,过程如图所示:
先用小饼干1满足小胃口孩子1,然后饼干3满足2,饼干5因为无法满足小孩7,所以饼干后移到饼干9,而饼干9可以满足小孩7.
这里局部最优就是小饼干投喂小胃口的孩子,不会造成浪费,而全局最优就是尽可能多的喂饱孩子。
class Solution {
public:
int findContentChildren(vector& g, vector& s) {
sort(g.begin(),g.end());
sort(s.begin(),s.end());
int index = 0;
for(int i = 0;i < s.size();i++) {
if(index < g.size() && g[index] <= s[i]) {
index++;
}
}
return index;
}
};
第二种(大饼干满足大胃口):
最大的饼干要是满足胃口最大的孩子的话,那么一定也能满足胃口小的孩子,但是用大饼干满足小胃口孩子会造成浪费,所以用大饼干满足大孩子。
这里局部最优就是大饼干投喂大胃口的孩子,不会造成浪费,而全局最优就是尽可能多的喂饱孩子。
class Solution {
public:
int findContentChildren(vector& g, vector& s) {
sort(g.begin(), g.end());
sort(s.begin(), s.end());
int index = s.size() - 1; // 饼干数组的下标
int result = 0;
for (int i = g.size() - 1; i >= 0; i--) { // 遍历胃口
if (index >= 0 && s[index] >= g[i]) { // 遍历饼干
result++;
index--;
}
}
return result;
}
};
题目链接:376. 摆动序列 - 力扣(LeetCode)
视频链接:贪心算法,寻找摆动有细节!| LeetCode:376.摆动序列_哔哩哔哩_bilibili
如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。
例如, [1, 7, 4, 9, 2, 5]
是一个 摆动序列 ,因为差值 (6, -3, 5, -7, 3)
是正负交替出现的。
[1, 4, 7, 2, 5]
和 [1, 7, 4, 5, 5]
不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。
给你一个整数数组 nums
,返回 nums
中作为 摆动序列 的 最长子序列的长度 。
示例 1:
输入:nums = [1,7,4,9,2,5] 输出:6 解释:整个序列均为摆动序列,各元素之间的差值为 (6, -3, 5, -7, 3)
示例 2:
输入:nums = [1,17,5,10,13,15,10,5,16,8] 输出:7 解释:这个序列包含几个长度为 7 摆动序列。 其中一个是 [1, 17, 10, 13, 10, 16, 8] ,各元素之间的差值为 (16, -7, 3, -3, 6, -8) 。
以示例二为例,它的操作过程如图所示:
在实际操作的时候,并不用把这些点给删除了,只需要统计峰值数量就行。
本题需要考虑三种情况:
1、上下坡中间有平坡
以[1,2,2,2,1]为例,它的摆动序列长度是3,要么就删除左边三个2,要么就删除右边三个2.
2、就有首和尾
如果出现了数组中就有两个元素,按照之前的想法是无法计算的,因为要算的话至少需要三个元素,所以这种情况就假设数组开头那个数的前面还有一个数字,这个数字跟数组开头第一个数字相同,默认最后面有一个峰值,这样的话就能延续以上规则继续运行了。
也可以把这种情况作为单独的一种情形拿出来,就判断一下数组里只有俩元素,它俩要是不相同就直接返回二就行了。
3、单调坡有平坡(最容易被忽视的一种情况)
这个摆动序列的长度为2,如果按之前的想法来思考,它的摆动序列的长度就为3了,因为prediff在2那里也记录了,但它却不是最大峰值,所以出错了,所以我们要在出现坡度有摆动变化的时候再更新prediff才可以。
class Solution {
public:
int wiggleMaxLength(vector& nums) {
if(nums.size() <= 1) return nums.size();
int curDiff = 0;
int preDiff = 0;
int result = 1;
for(int i = 0;i < nums.size() - 1;i++) {
curDiff = nums[i + 1] - nums[i];
if((preDiff >= 0 && curDiff < 0) || (preDiff <= 0 && curDiff >0)) {
result++;
preDiff = curDiff;
}
}
return result;
}
};
题目链接:53. 最大子数组和 - 力扣(LeetCode)
视频链接:贪心算法的巧妙需要慢慢体会!LeetCode:53. 最大子序和_哔哩哔哩_bilibili
给你一个整数数组 nums
,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4] 输出:6 解释:连续子数组 [4,-1,2,1] 的和最大,为 6
本题贪心是遇到连续和为负数的时候,从下一个元素开始重新累加,因为毕竟负数会让和越来越小,所以该断得断才能不受其乱。连续和为正数就继续保留,因为不论这个和是大是小,它都会对后面起一个增大的作用。当然了,肯定会有一个变量在持续更新最大值的。
断的话一定是遇到连续和为负数,而不是遇到负数,这两个区别很大的。
class Solution {
public:
int maxSubArray(vector& nums) {
int result = INT32_MIN;
int count = 0;
for(int i = 0;i < nums.size();i++) {
count += nums[i];
if(count > result) {
result = count;
}
if(count <= 0) count = 0;
}
return result;
}
};