[LeetCode 45] 跳跃游戏2 (Ⅱ)
题面:
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 & right
size_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;
}