leetcode55. 跳跃游戏 45. 跳跃游戏 II


法一:我写的第一份通过的代码,问题在于重复更新浪费不少时间,内层循环可能会重复更新许多已经确定不是最优解的位置。
class Solution {
public int jump(int[] nums) {
int n = nums.length;
int[] dp = new int[n]; //dp[i]表示到达此处的最小跳跃次数
for(int i = 1;i < n;++i) dp[i] = 100001;
for(int i = 0;i < n;++i){
for(int j = i + 1;j <= i + nums[i] && j < n;++j){
dp[j] = Math.min(dp[j],dp[i] + 1);
}
}
return dp[n-1];
}
}
以下是优化后的代码。
这个优化的核心思路基于一个观察:DP数组在跳跃游戏问题中是单调递增的。也就是说,位置j的最小跳跃次数不会多于位置j+1的最小跳跃次数。
反向遍历的优势:从i+nums[i]向回遍历到i+1,这样一旦遇到某个j满足dp[j] <= dp[i] + 1,就意味着j以及比j更靠近i的位置都已经获得了比从i跳转过去更优(或相等)的解。既然DP数组是单调的,那么就没有必要继续向更靠近i的位置遍历了。
减少重复计算:你的原始代码中,即使某个位置j已经通过之前的i获得了最小跳跃次数,后续的i仍然会尝试更新它。优化后的代码通过反向遍历和判断条件,显著减少了这类冗余操作。
class Solution {
public int jump(int[] nums) {
int n = nums.length;
int[] dp = new int[n]; // dp[i]表示到达此处的最小跳跃次数
for(int i = 1; i < n; i++) dp[i] = 100001;
for(int i = 0; i < n; i++) {
// 关键优化:从最远点向回遍历,遇到已更新位置可提前结束
for(int j = Math.min(n - 1, i + nums[i]); j > i; j--) {
// 如果j位置已经是最优解,说明更近的位置也已经更新过
if(dp[j] <= dp[i] + 1) break;
dp[j] = dp[i] + 1;
}
}
return dp[n - 1];
}
}
法二:
注意:不是在无路可走的那个位置造桥,而是当发现无路可走的时候,时光倒流到能跳到最远点的那个位置造桥。
换句话说,在无路可走之前,我们只是在默默地收集信息,没有实际造桥。
当发现无路可走的时候,才从收集到的信息中,选择最远点造桥。
所建造的这座桥的左端点(起跳位置)在我们当前走的这座桥的中间,而不是桥的末尾。注意nextRight = Math.max(nextRight, i + nums[i]);
class Solution {
public int jump(int[] nums) {
int ans = 0;
int curRight = 0; // 已建造的桥的右端点
int nextRight = 0; // 下一座桥的右端点的最大值
for (int i = 0; i < nums.length - 1; i++) {
// 遍历的过程中,记录下一座桥的最远点
nextRight = Math.max(nextRight, i + nums[i]);
if (i == curRight) { // 无路可走,必须建桥
curRight = nextRight; // 建桥后,最远可以到达 next_right
ans++;
}
}
return ans;
}
}
浙公网安备 33010602011771号