第八章 贪心算法 part02
● 122.买卖股票的最佳时机II
● 55. 跳跃游戏
● 45.跳跃游戏II
详细布置
122.买卖股票的最佳时机II
本题解法很巧妙,大家可以看题思考一下,在看题解。
算法的思路是:
对于每一天,我们都尝试在最低的价格买入,然后在价格最高的时刻卖出。如果我们能够持续执行这个操作,我们就可以获得最大的利润。
从第二天开始遍历价格数组,计算当前价格与前一天价格的差值。如果差值是正数,代表今天的价格比昨天高,我们就可以在昨天买进并在今天卖出,此时可以得到差值的收益,否则我们不进行任何操作。
最后累加所有的差值即可得到总的收益。
这个算法的时间复杂度是O(n),其中n是价格数组的长度。
Java:
贪心:
// 贪心思路
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;
}
}
代码解读
result += Math.max(prices[i] - prices[i - 1], 0);
价格的差值与0取最大值,实现了负收益不操作
55. 跳跃游戏
https://programmercarl.com/0055.%E8%B7%B3%E8%B7%83%E6%B8%B8%E6%88%8F.html
算法的思路是:
从第一个位置开始,不断更新当前位置能够覆盖的最大范围,如果最终覆盖范围可以到达最后一个位置,则返回true;否则返回false。
具体来说,代码中使用coverRange来表示当前位置能够覆盖的最大范围,初始值为0。然后通过循环迭代数组中的每个位置,更新coverRange为当前位置加上该位置能够跳跃的最大长度。如果coverRange能够到达数组的最后一个位置,则返回true;否则返回false。
这个算法的时间复杂度是O(n),其中n是数组的长度。
Java
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;
}
}
代码解读
为什么是for中是i <= coverRange而不是i <= num.length?
在这段代码中,循环条件使用的是 i <= coverRange
而不是 i <= nums.length
是因为我们需要在当前位置 i 的覆盖范围内更新最大的覆盖范围,而不是简单地遍历整个数组的长度。
具体来说,我们通过 coverRange = Math.max(coverRange, i + nums[i])
这行代码来更新当前覆盖范围 coverRange
,保证其能够覆盖更远的位置。因此,在循环中,我们需要遍历当前位置 i
到 coverRange
范围内的所有位置,并更新最大的覆盖范围。只有这样才能保证我们在每个位置都做出最优的选择,以便判断是否能够到达最后一个位置。
因此,循环条件应该是 i <= coverRange
而不是 i <= nums.length
。
45.跳跃游戏II
本题同样不容易想出来。贪心就是这样,有的时候 会感觉简单到离谱,有时候,难的不行,主要是不容易想到。
https://programmercarl.com/0045.%E8%B7%B3%E8%B7%83%E6%B8%B8%E6%88%8FII.html
Java
// 版本一
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;
}
}