文章目录
前言
贪心,找到局部解。
一、122.买卖股票的最佳时机II
其实我们需要收集每天的正利润就可以,收集正利润的区间,就是股票买卖的区间,而我们只需要关注最终利润,不需要记录区间。
那么只收集正利润就是贪心所贪的地方!
局部最优:收集每天的正利润,全局最优:求得最大利润。
局部最优可以推出全局最优,找不出反例,试一试贪心!
// 贪心思路
class Solution {
public int maxProfit(int[] prices) {
int result = 0;
for (int i = 1; i < prices.length; i++) {
result += Math.max(prices[i] - prices[i - 1], 0);
}
return result;
}
}
二、55. 跳跃游戏
一开始看到题目的思路是,找到一个在跳跃范围内最大的数,去加上他,一直循环,直到判断能否到达终点;万万没想到,思路更直接的是直接验算覆盖范围;虽然代码表示上可能一样,但后者的思路更贪心。
贪心算法局部最优解:每次取最大跳跃步数(取最大覆盖范围),整体最优解:最后得到整体最大覆盖范围,看是否能到终点。
局部最优推出全局最优,找不出反例,试试贪心!
//注意这里,是用i+nums[i]直接赋值;
//判断两者的最大值
//这里是大于等于,不是等于
class Solution {
public boolean canJump(int[] nums) {
if(nums.length == 1){
return true;
}
int cover = 0;
for(int i = 0;i <= cover;i++){
//注意这里,是用i+nums[i]直接赋值;
//判断两者的最大值
cover = Math.max(i+nums[i],cover);
//这里是大于等于,不是等于
if(cover >= nums.length-1) return true;
}
return false;
}
}
三、45.跳跃游戏II
刚开始以为在上一题加一个判断就能搞定:
class Solution {
public int jump(int[] nums) {
if(nums.length == 1){
return 0;
}
int cover = 0;
int res = 0;
for(int i = 0;i <= cover;i++){
if(i + nums[i] > cover){
res ++;
}
cover = Math.max(i+nums[i],cover);
if(cover >= nums.length-1) break;
}
return res;
}
}
在这里翻车了:
尝试解这个例子,发现了问题,因为是随着cover来进行循环的,每一次只考虑cover和i+nums[i]的哪个大, 这样得出的res只能是当前的最大覆盖范围,但题目要求的是最小跳跃次数!
所以真正解题的时候,要从覆盖范围出发,不管怎么跳,覆盖范围内一定是可以跳到的,以最小的步数增加覆盖范围,覆盖范围一旦覆盖了终点,得到的就是最小步数!
这里需要统计两个覆盖范围,当前这一步的最大覆盖和下一步最大覆盖。
如果移动下标达到了当前这一步的最大覆盖最远距离了,还没有到终点的话,那么就必须再走一步来增加覆盖范围,直到覆盖范围覆盖了终点。
版本一和二的区别:
其精髓在于控制移动下标 i 只移动到 nums.size() - 2 的位置,所以移动下标只要遇到当前覆盖最远距离的下标,直接步数加一,不用考虑别的了。
// 版本一
class Solution {
public int jump(int[] nums) {
if (nums == null || nums.length == 0 || nums.length == 1) {
return 0;
}
//记录跳跃的次数
int count=0;
//当前的覆盖最大区域
int curDistance = 0;
//最大的覆盖区域
int maxDistance = 0;
for (int i = 0; i < nums.length; i++) {
//在可覆盖区域内更新最大的覆盖区域
maxDistance = Math.max(maxDistance,i+nums[i]);
//说明当前一步,再跳一步就到达了末尾
if (maxDistance>=nums.length-1){
count++;
break;
}
//走到当前覆盖的最大区域时,更新下一步可达的最大区域
if (i==curDistance){
curDistance = maxDistance;
count++;
}
}
return count;
}
}
// 版本二
class Solution {
public int jump(int[] nums) {
int result = 0;
// 当前覆盖的最远距离下标
int end = 0;
// 下一步覆盖的最远距离下标
int temp = 0;
for (int i = 0; i <= end && end < nums.length - 1; ++i) {
temp = Math.max(temp, i + nums[i]);
// 可达位置的改变次数就是跳跃次数
if (i == end) {
end = temp;
result++;
}
}
return result;
}
}
总结
贪心