跳跃游戏(题号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 * 1040 <= nums[i] <= 105
链接
解释
这波啊,这波是经典贪心。
由于是中等题,笔者第一时间给想复杂了,以为又是那种有经典递归和DP两种结局方案的题。
虽然题目规定数组长度有10 ^ 4,但笔者依然是尝试了一下递归,不过这次失败得比较早,在第75个用例上GG了。
之后仔细想了想,其实题目没有那么复杂,问题的是能不能走到最后,而不是有几种方法能走到最后,那就简单很多了。
比方说当前的位置是i,那么当前位置可以达到的最远位置就是i + nums[i],当前位置加上当前可走的最大长度就是当前位置的最远位置。
知道这一点之后就一目了然了,每次循环更新一个最大位置,如果当前的i和最大元素相等或者比最大元素大,那就证明走到尽头了,一步也迈不出去。
或者说当前的最大位置已经超过数组的最大index(nums.length - 1),那就证明可以走到最后了,后面的都不用看了,直接返回就行。
自己的答案(递归)
十分简单的递归,也十分简单的失败了。
var canJump = function(nums) {
var res = false
function DFS(index) {
if (index === nums.length - 1) return res = true
if (res || (index >= nums.length)) return;
for (let i = 1; i <= nums[index]; i++) {
DFS(index + i)
}
}
DFS(0)
return res
};
递归函数中先是两个终止条件,如果当前的index到了数组末尾,直接返回,否则如果res为true,也就是前面已经有位置可以走到最后了,也返回,如果当前的index已经比可以跳的位置更大,直接GG。
自己的答案(贪心)
思路上面说过了,先看看官方的答案👇:
var canJump = function(nums) {
var n = nums.length
rightmost = 0
for (let i = 0; i < n; i++) {
if (i <= rightmost) {
rightmost = Math.max(rightmost, i + nums[i])
if (rightmost >= n - 1) {
return true
}
}
}
return false
};
官方并没有给出JavaScript的答案,👆的答案是笔者根据C++的答案翻译过来的。
再看看笔者自己的答案👇:
var canJump = function(nums) {
var maxLength = 0
len = nums.length
for (let i = 0; i < len; i++) {
maxLength = Math.max(maxLength, i + nums[i])
if (maxLength >= len - 1) return true
if (maxLength <= i) return false
}
};
笔者这里的思路和官方有点不一样,但哪种比较好也不好说。
官方这里在遇到终止的情况之后就不进行任何赋值操作了,直接让后序的for循环走完,笔者这里给了剪枝操作,如果遇到没法再跳的情况下,直接return false。
这一块感觉比官方好些,因为在没有找到答案之前,笔者的答案和官方答案都需要进行这两个判断:
maxLength <= i
maxLength >= len - 1
并且都需要利用Math.max()来给maxLength赋值,本质上是没有差别的。
从实际执行情况来说也确实好些,可以说是一种优化吧。
当然了,这题也可以使用DP来解决,不过有点过于复杂了,而且性能方面也不是很好,这里不推荐。
更好的方法
无
PS:想查看往期文章和题目可以点击下面的链接:
这里是按照日期分类的👇
经过有些朋友的提醒,感觉也应该按照题型分类
这里是按照题型分类的👇