什么是贪心?
本质就是选择每一阶段最优,从而达到全局最优。例如取n张钞票获得最大值,每次就取最大面额。
贪心的套路(什么时候使用贪心)
贪心算法没有固定的套路,贪心也不需要去严格证明,手动模拟一下感觉可以局部最优推出全局最优即可(想不到反例)
贪心一般解题步骤(四步)
其实平常很难真正按照这四步去思考,做题只需要想清楚局部最优,能推出全局最优就行了。
题目介绍:
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 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。
示例 2:
输入: g = [1,2], s = [1,2,3]
输出: 2
解释:
你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。
你拥有的饼干数量和尺寸都足以让所有孩子满足。
所以你应该输出2.
个人思路:
对两个数组进行排序,然后双指针从小开始逐个分配饼干即可
class Solution {
public int findContentChildren(int[] g, int[] s) {
Arrays.sort(g);
Arrays.sort(s);
int resutl = 0;
for (int i = 0, j = 0; i < g.length && j < s.length; ) {
if (g[i] <= s[j]) {
i++;
j++;
resutl++;
} else j++;
}
return resutl;
}
}
题解思路:
题解和上述思路差不多,不过它是用尽可能大的饼干满足胃口较大的孩子,而上述思路是用尽可能小的饼干满足胃口较小的孩子
题目介绍:
如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 **摆动序列 。**第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。
[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) 。
示例 3:
输入:nums = [1,2,3,4,5,6,7,8,9]
输出:2
这道题大致想了一下,没有思路,个人想到使用递归回溯,但时间复杂度可能比较大
题解解析:
本题可以先通过话趋势图来构思
局部最优:删除单调坡上的节点(不包括单调坡两端的节点),那么这个坡度就可以有两个局部峰值
整体最优:整个序列有最多的局部峰值,从而达到最长摆动序列
实际上,我们并不需要删除,只需要记录长度即可。判断峰值可用 前差值 * 后差值 < 0统计。prediff < 0 && curdiff > 0
或者 prediff > 0 && curdiff < 0
条件
本题要考虑三种特殊情况:
上下坡中有平坡
如果我们要删除的是左边两个2,我们注意到第一个2处,prediff > 0 && curdiff = 0
,而最后一个2处,prediff = 0 && curdiff < 0
,所以,我们可以将后者条件作为收录峰值的条件之一。可得:(preDiff <= 0 && curDiff > 0) || (preDiff >= 0 && curDiff < 0)
数组首尾两端
我们发现我们的条件需要至少三个元素才可以判断,如果只有两个元素该怎么办呢?
法一:可以在首元素前面假设有一个和首元素一样的元素,使得一开始的prediff = 0
,这样再根据此前的条件判断就可以统计峰值了,但是我们还发现其实相比结果还是差1,所以我们一开始就把最右端看作一个峰值,默认初始化result = 1起。(这种写法较好,可以使代码较简略,统一整个代码逻辑)
法二:直接通过if判断写死。(其他情况不能直接初始化result = 2,可能会有一路平坡的样例,得初始化result = 1
,最终result>1
则result++
此时可得到以下代码,但是通不过。
// 版本一
class Solution {
public:
int wiggleMaxLength(vector<int>& 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;
}
};
我们还得考虑情况三
单调有平坡
出现这种问题的原因是我们实时更新prediff,其实并没有必要,可以发现实时更新会在最后一个2处出现错误,我们应该在坡峰发生变化的时候改变prediff,其他时候保持之前的状态就好(
此前遇到过非破峰的情况:
所以我们需要小改代码即可
//版本2
class Solution {
public:
int wiggleMaxLength(vector<int>& 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; // 注意这里,只在摆动变化的时候更新prediff
}
}
return result;
}
};
Java版本:代码看起来挺简单,实际逻辑还是值得细品
class Solution {
public int wiggleMaxLength(int[] nums) {
int prediff = 0;
int curdiff = 0;
int result = 1;
for (int i = 0; i < nums.length - 1; i++) {
curdiff = nums[i + 1] - nums[i];
if (prediff <= 0 && curdiff > 0 || prediff >= 0 && curdiff < 0) {
result++;
prediff = curdiff;
}
}
return result;
}
}
题目介绍:
给你一个整数数组 nums
,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例 2:
输入:nums = [1]
输出:1
示例 3:
输入:nums = [5,4,-1,7,8]
输出:23
个人思路:
使用以前学过的双指针法,类似滑动模块思路解决。
关键逻辑,sum < 0
时,重新确定新区间,一路遍历一路比较sum和result即可
class Solution {
public int maxSubArray(int[] nums) {
int slow = 0;
int fast = 0;
int sum = 0;
int result = Integer.MIN_VALUE;
while (fast < nums.length) {
if (sum < 0) {//sum<0,必须砍掉才能找到更大区间和
slow = fast++;
sum = nums[slow];
} else sum += nums[fast++];
if (sum > result)
result = sum;
}
return result;
}
}
ast < nums.length) {
if (sum < 0) {//sum<0,必须砍掉才能找到更大区间和
slow = fast++;
sum = nums[slow];
} else sum += nums[fast++];
if (sum > result)
result = sum;
}
return result;
}
}