题目概述:
题目链接:点我做题
题解:
一、动态规划
因为我看的dp标签进的这个题,所以先想了个动态规划的方法,思路主要是这样的:
定义一个size个元素的dp数组,dp[i]=true,when当前位置可以通过前面的跳跃到达
,dp[i]=false,when当前位置不可以通过前面的跳跃到达
,先使得dp[0]=true;
,表明从0位置出发;
然后根据nums[0]的值,更新
d
p
[
1
]
到
d
p
[
m
i
n
(
s
i
z
e
−
1
,
0
+
n
u
m
s
[
0
]
)
]
dp[1]到dp[min(size - 1, 0 + nums[0])]
dp[1]到dp[min(size−1,0+nums[0])]都为true,表明这些位置可以到达,
然后从1开始往后遍历,每一个位置都先检查一下是否已经是true了,如果是,则可以进去仿照0位置更新
d
p
dp
dp数组,由于STL的vector默认初始化的初始值是0,所以
d
p
[
s
i
z
e
−
1
]
dp[size - 1]
dp[size−1]默认会被初始化成
f
a
l
s
e
false
false,遍历完了以后就返回
d
p
[
s
i
z
e
−
1
]
dp[size - 1]
dp[size−1]的值就可以了。
代码:
class Solution {
public:
bool canJump(vector<int>& nums)
{
int size = nums.size();
vector<bool> dp(size);
dp[0] = true;
for (int i = 0; i <= nums[0]; ++i)
{
if (i >= size)
{
break;
}
dp[i] = true;
}
for (int i = 1; i < size - 1; ++i)
{
if (dp[i] == true)
{
for (int j = 1; j <= nums[i]; ++j)
{
if (i + j >= size)
{
break;
}
dp[i + j] = true;
}
}
if (dp[size - 1] == true)
{
break;
}
}
return dp[size - 1];
}
};
这个代码有一个可以优化的地方,如果进来发现dp[i]等于false了,应该直接return false;
,因为连i位置都不能通过前面的跳跃到达的话,那么是不可能到达size-1这个位置的,因为每轮其实都会跟更新当前能到达的最右位置的dp为true,如果你进入的位置是false,说明前一轮能到达的最右位置在这之前,那么你是永远到达不了这个位置的,既然你到达不了这个位置,那后面的位置肯定也到达不了,因为规则规定你可以跳1步、跳2步、跳3步…跳nums[i]步,能到达的位置一定是连续的,这位置到达不了那下一个位置肯定也到达不了,最右到达位置就不会更新了,所以size -1肯定也到达不了
时间复杂度:
O
(
n
∗
m
a
x
i
(
n
u
m
s
[
i
]
)
)
O(n*max_{i}(nums[i]))
O(n∗maxi(nums[i]))
空间复杂度:
O
(
n
)
O(n)
O(n)
二、贪心算法
其实注意到的每一个位置都会实时更新最右到达位置这点的时候,就应该想到这个方法。
维护一个最右到达位置,初始值是0,表明此时0下标可以到达;
然后通过位置0的nums[0]值更新最右到达位置,此时最右到达位置为
m
a
x
(
0
,
0
+
n
u
m
s
[
0
]
)
max(0, 0 +nums[0])
max(0,0+nums[0]);
然后从下标为1的位置出发,遍历一遍nums数组,先检查此时的最右到达位置是否覆盖了次位置,如果没有覆盖,正如我们在动态规划里说的一样,直接返回break出去返回false就行;
否则进去更新最右到达位置为:
r
i
g
h
t
m
a
x
=
m
a
x
(
r
i
g
h
t
m
a
x
,
i
+
n
u
m
s
[
i
]
)
;
rightmax = max(rightmax, i + nums[i]);
rightmax=max(rightmax,i+nums[i]);,
每轮循环最后,检查当前最优到达位置是否已经大于
s
i
z
e
−
1
size - 1
size−1,如果大于了,说明已经可以到达尾巴了,返回
t
r
u
e
true
true就可以。
代码:
class Solution {
public:
bool canJump(vector<int>& nums)
{
//贪心算法
//维护一个最大到达下标rightmax,初始值为0
//如果我们的当前下标小于最大到达下标,则表明当前下标可以通过前面的跳跃到达
//然后更新最大到达下标为rightmax = rightmax + nums[i]
//然后检查rightmax是否大于size - 1
//如果大于 则return true
//如果当前下标大于最大到达下标 说明当前位置已经不可以到达 直接return false
int size = nums.size();
int rightmax = 0;
for (int i = 0; i < size; ++i)
{
if (i <= rightmax)
{
rightmax = max(rightmax, i + nums[i]);
}
else
{
break;
}
if (rightmax >= size - 1)
{
return true;
}
}
return false;
}
};