leetcode
55. 跳跃游戏
给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
示例 1:
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
示例 2:
输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。
提示:
1 <= nums.length <= 3 * 104
0 <= nums[i] <= 105
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/jump-game
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
本人想法
本题我递推出状态转移方程后用的循环。
class Solution {
public boolean canJump(int[] nums) {
boolean[] F = new boolean[nums.length];
F[nums.length-1]=true;
for(int i = nums.length -1;i>=0;i--){
//从后往前
int j = 0;
while(j<=nums[i]){
if(i+j<nums.length && F[i+j] ){
F[i]=true;
break;
}
j++;
}
}
return F[0];
}
}
跑出来速度就超过3%,我就想优化一下。设i
是离0
最近的true
,那在此时记下一个量j=0
,每次往前走一个就j++
,如果当前nums[i-j]>=j
就证明i-j
可以走到i
,就可以把i-j
也置为true
,也就是把i-j
当成新的i
,并重新令j=0
。最后看一轮走完i
最后是不是0
,是的话就说明0
是true
也就是可以走到
class Solution {
public boolean canJump(int[] nums) {
// boolean ret = false;
int n = nums.length;
int i = n-1;
int j = 0;
while(true){
j++;
if(i-j<0){
break;
}
else if(nums[i-j]>=j){
i = i-j;
j=0;
}
}
return i==0;
}
}
因为就走了一趟,时间复杂度应该为O(N),空间复杂度为O(1)。
官方题解
官方直接给出了贪心方法(呃呃因为在动态规划题目里就先入为主了,虽然优化后的方法有贪心的思想。),直接遍历一遍,记录一个量最多能到达的距离:rightmost
,每次都取rightmost
和i + nums[i]
的最大值。好机智。时空复杂度和我相同,也是时间复杂度
O
(
N
)
O(N)
O(N),空间复杂度
O
(
1
)
O(1)
O(1)。学习学习。
public class Solution {
public boolean canJump(int[] nums) {
int n = nums.length;
int rightmost = 0;
for (int i = 0; i < n; ++i) {
if (i <= rightmost) {
rightmost = Math.max(rightmost, i + nums[i]);
if (rightmost >= n - 1) {
return true;
}
}
}
return false;
}
}
作者:LeetCode-Solution
链接:https://leetcode.cn/problems/jump-game/solution/tiao-yue-you-xi-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
45. 跳跃游戏 II
给你一个非负整数数组 nums ,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
假设你总是可以到达数组的最后一个位置。
示例 1:
输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
示例 2:
输入: nums = [2,3,0,1,4]
输出: 2
提示:
1 <= nums.length <= 104
0 <= nums[i] <= 1000
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/jump-game-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
我的思路
方案一
设F[i]是从0→i的最小步数。不能到达则置为n。初始F均为n;
F
(
0
)
=
0
F(0)=0
F(0)=0
F
(
j
)
=
1
,
f
o
r
j
f
r
o
m
1
t
o
n
u
m
s
[
0
]
F(j)=1, for \ \ j\ \ from\ \ 1 \ \ to \ \ nums[0]
F(j)=1,for j from 1 to nums[0]
👉对每个i
F
(
j
)
=
m
i
n
{
F
(
j
)
,
F
(
i
)
+
1
}
,
f
o
r
j
f
r
o
m
i
t
o
i
+
n
u
m
s
[
i
]
F(j) = min\{F(j), F(i)+1\}, for \ \ j \ \ from \ \ i \ \ to \ \ i + nums[i]
F(j)=min{F(j),F(i)+1},for j from i to i+nums[i]
i
作为0
到n-1
的索引,j
对每个i
都从0
到nums[i]
,以i
为起点往后走j
个,走过的都能从i
走1步
就到达,所以将其F[j]
置为F(i)+1
和F(j)
自己的最小值。F[j]
初始化为n
。
为了便于理解 写了个例子
显然这是会重复访问变量的。而且显然F(i)一旦写下后(除了初始化为n)就会最小,不可能再改,但是用这个方法会被重复访问到,然后进行min的判断操作。。
所以优化一下:
代码
class Solution {
public int jump(int[] nums) {
int n = nums.length;
int[] F = new int[n];
F[0]=0;
if(n==1)return 0;
F[1]=nums[0];
if(F[1]>=n-1){return 1;}
for(int i = 2;i<n;i++){
F[i]=sub(nums,F[i-2],F[i-1]);
if(F[i]>=n-1){return i;}
}
return n-1;
}
public int sub(int[] nums,int start,int end){//[start,end]区间
int maxStep = 0;
for(int j = start;j<end+1;j++){
maxStep = Math.max(maxStep,j+nums[j]);
}
return maxStep;
}
}
官方题解
看了一下答案,使用贪心思想,每次往后走都选择能走得最远的那个点。之后走到那个 点 step+1,再从当前点往后走,。。。循环。。
等同于把我的start
end
也就是F[i-2],F[i-1]
直接用两个变量end,maxPosition
记录了,相当于滚动数组的思想。(指代码上。总的来说设定、含义不太相同,但代码上基本相似。)由于相当于滚动数组,所以时间复杂度相同为O(n)
,其中 n
是数组长度。空间复杂度从O(n)变为O(1)。
class Solution {
public int jump(int[] nums) {
int length = nums.length;
int end = 0;
int maxPosition = 0;
int steps = 0;
for (int i = 0; i < length - 1; i++) {
maxPosition = Math.max(maxPosition, i + nums[i]);
if (i == end) {
end = maxPosition;
steps++;
}
}
return steps;
}
}
作者:LeetCode-Solution
链接:https://leetcode.cn/problems/jump-game-ii/solution/tiao-yue-you-xi-ii-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。