122. 买卖股票的最佳时机II
题目链接:122. 买卖股票的最佳时机 II
思路:题目中利润是可以分解的。
加入第0天买入,第三天卖出,利润为price[3] - price[0]。其利润可以分解成(prices[3] - prices[2]) + (prices[2] - prices[1]) + (prices[1] - prices[0])。
使用贪心算法收集所有的正利润,最终结果就是最大利润。
(本题也可以使用动态规划来解决)
class Solution {
public int maxProfit(int[] prices) {
int profit = 0;
// 遍历整个股票交易日价格列表,策略是所有上涨交易日都买卖(赚到所有利润),
// 所有下降交易日都不买卖(永不亏钱)
for (int i = 1; i < prices.length; i++) {
// 设 tmp 为第 i-1 日买入与第 i 日卖出赚取的利润
int tmp = prices[i] - prices[i - 1];
// 当该天利润为正 tmp > 0,则将利润加入总利润 profit
if (tmp > 0) profit += tmp;
// 可以简写
// profit += Math.max(prices[i] - prices[i - 1], 0);
}
// 遍历完成后,返回总利润 profit
return profit;
}
}
收集正利润的区间,就是股票买卖的区间,而我们只需要关注最终利润,不需要记录区间。
那么只收集正利润就是贪心所贪的地方!
局部最优:收集每天的正利润;全局最优:求得最大利润。
本题中理解利润拆分是关键点! 不要整块的去看,而是把整体利润拆为每天的利润。
55. 跳跃游戏
题目链接:55. 跳跃游戏
思路:可以将问题转化为跳跃的最大覆盖范围能不能覆盖到终点。
**局部最优:**每一步的最大覆盖范围。
**整体最优:**最大覆盖范围能否覆盖到终点。
i每次移动只能在cover的范围内移动,每移动一个元素,cover得到该元素数值(新的覆盖范围)的补充,让i继续移动下去。
class Solution {
public boolean canJump(int[] nums) {
if (nums.length == 1) {
return true; // 只有一个元素,就是能达到
}
// 覆盖范围, 初始覆盖范围应该是0,因为下面的迭代是从下标0开始的
int coverRange = 0;
// 在覆盖范围内更新最大的覆盖范围
for (int i = 0; i <= coverRange; i++) {
coverRange = Math.max(coverRange, i + nums[i]);
if (coverRange >= nums.length - 1) {
return true;
}
}
return false;
}
}
这道题目关键点在于:不用拘泥于每次究竟跳几步,而是看覆盖范围,覆盖范围内一定是可以跳过来的,不用管是怎么跳的。
45. 跳跃游戏II
题目链接:45. 跳跃游戏 II
思路:同样要看最大覆盖范围,但是要记录下一步覆盖的最远距离坐标,如果下标到达了当前的最大覆盖距离,仍然没有到达终点,那么就要加一步,开始下一步的最大覆盖距离。
真正解题的时候,要从覆盖范围出发,不管怎么跳,覆盖范围内一定是可以跳到的,以最小的步数增加覆盖范围,覆盖范围一旦覆盖了终点,得到的就是最小步数!
这里需要统计两个覆盖范围,当前这一步的最大覆盖范围和下一步最大覆盖范围。
class Solution {
public int jump(int[] nums) {
int cover = 0; // 当前最大覆盖范围
int nextCover = 0; // 下一步最大覆盖范围
int res = 0; // 结果
for (int i = 0; i < nums.length - 1; i++) {
// 更新下一步最大覆盖范围
nextCover = Math.max(nextCover, i + nums[i]);
if (i == cover) {
// 走到当前最大覆盖距离,增加一步,更新最大覆盖范围
res++;
cover = nextCover;
}
}
return res;
}
}
其精髓在于控制移动下标i只移动到
nums.length - 2
的位置,所以移动下标只要遇到当前覆盖最远距离的下标,直接步数加一,不用考虑别的了。