1. 55.跳跃游戏
1.1. 题目
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个位置
1.1.1. 举例
输入: [2,3,1,1,4]
输出: true
解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。
1.2. 思路
- 这是一个大问题可以化成小问题的题目
- 很明显,对于输入的那个串(举例:[2,3,1,1,4]), 每个元素都有多个对应值, 比如2对应 0,1,2
- 很明显: 这种一对多的形式可以使用回溯法(举例参见我的另一篇博客Leetcode括号生成 + 电话号码
- 但是回溯法的时间成本太高了(回溯法说到底就是个暴力遍历破解 )
- 观察发现回溯法中的很多子问题是重复计算的
- 为了解决重复的子问题结算导致浪费: 使用动态规划的算法, 动态规划有两种思路
- 1.自顶向下思路: 自顶向下说到底就是大的问题化小的问题, 小的问题化更小的问题(本质是不用重复计算的递归回溯算法)
- 2.自底向上思路: 相比与自顶向下算法, 自底向上更优秀一点,
-
- 不用递归(或用栈)节省了空间
- 自底向上思路: 自底向上说到底就是在大问题可以化成小问题的基础想, 从小问题出发, 逐渐扩大, 最终扩大到原本问题的规模
-
1.3. 代码
public boolean canJump2(int[] nums) {
boolean[] can=new boolean[nums.length];// 默认为false
can[nums.length-1]=true;// 最尾为真(可跳)
int jumpMax=0;
for(int i=nums.length-2;i>=0;i--) {
jumpMax=Math.min(i+nums[i], nums.length-1);
for(int j=i+1;j<=jumpMax;j++)
if(can[j]) {
can[i]=true; break;
}
}
return can[0];
}
但是
明显不够优秀
- 观察发现:
for(int j=i+1;j<=jumpMax;j++)
if(can[j]) {
can[i]=true; break;
}
- 其实这个代码就是找足够大的数 能够到那些已经计算过能够到串尾的元素👇
1.3.1. 改进版的代码
public boolean canJump(int[] nums) {
int leftMostIndex=nums.length-1;// 初始化为最右元素
for(int i=nums.length-1;i>=0;i--) {// 从后往前
if(leftMostIndex<=nums[i]+i)//i处的元素够得到 '最左可达元素'
leftMostIndex=i;
}
return leftMostIndex==0;
}
妈的代码又短又厉害, 你咋这么牛咧👄
1.4. 参考link
- 更多更完整的在这里leetcode君