题面:
LeetCode 45 跳跃游戏2
数据范围:
1 ≤ n u m s . l e n g t h ≤ 1 0 4 1 \le nums.length \le 10^4 1≤nums.length≤104
0 ≤ n u m s [ i ] ≤ 1000 0 \le nums[i] \le 1000 0≤nums[i]≤1000
题目保证可以到达 n u m s [ n − 1 ] nums[n-1] nums[n−1]
思路 & code:
1. dp暴力思路
往前找,如果前面某个点能到当前点 i n d e x index index,及存在 k < i k<i k<i 使得 k + n u m s [ k ] ≥ i k+nums[k] \ge i k+nums[k]≥i,则更新 dp(dp[i]存储到当前 i i i 点所需最小步长), d p [ i ] = m i n ( d p [ i ] , d p [ k ] + 1 ) dp[i] = min(dp[i], dp[k]+1) dp[i]=min(dp[i],dp[k]+1)
时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)
size_t n = nums.size();
vector<int> dp(n, 0x3f3f3f3f);
dp[0] = 0;
for(int i = 1; i < n; ++i)for(int k = 0; k <= i; ++k)if(k + nums[k] >= i)dp[i] = min(dp[i], dp[k] + 1);
2. dp + 贪心 + 双指针:即第一个跳到当前点 i i i 的点 j j j,必是步数最少的。
**证明:**假设有一个点 p > j p > j p>j, 其跳到点 i i i 的步数小于点 j j j 跳到点 i i i 的步数。则有 d p [ p ] + 1 < d p [ j ] + 1 dp[p] + 1 < dp[j] + 1 dp[p]+1<dp[j]+1, 即 d p [ p ] < d p [ j ] dp[p] < dp[j] dp[p]<dp[j]。由于 d p [ k ] dp[k] dp[k] 存储的是跳到 k k k 点的最短步长,又 j < p j < p j<p, 故能跳到 p p p 点的一定也能跳到 j j j 点,即 d p [ j ] ≤ d p [ p ] dp[j] \le dp[p] dp[j]≤dp[p] 的,故原式不成立。所以第一个跳到当前点i的点j必是步长最小的。
时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)
size_t n = nums.size(), j = 0;
vector<int> dp(n, 0);
for(size_t i = 1; i < n; ++i) {while(j + nums[j] < i) ++j;dp[i] = dp[j] + 1;
}
return dp[n-1];
3. 贪心+滑动窗口
维护一个当前能到达的最大边界(窗口),每次更新边界时,步数++。通俗理解就是因为当前的最大边界就是这一步能跳到的,如果想跳出更大的,则需要继续跳一步才行。
时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( 1 ) O(1) O(1)
int jump(vector<int>& nums) {/* 维护两个端点,left & rightsize_t n = nums.size(), left = 0, right = 0;size_t maxPos = 0, step = 0;while(right < n - 1) {for(auto i = left; i <= right; ++i)maxPos = max(maxPos, i + nums[i]);left = right + 1;right = maxPos;++ step;}return step;*//* 优化, left其实没有贡献, 一遍遍历只维护right即可*/size_t n = nums.size(), step = 0;size_t right = 0, maxPos = 0;for(size_t i = 0; i < n - 1; ++i) {maxPos = max(maxPos, i + nums[i]);if(i == right) {right = maxPos;++ step;}}return step;
}