这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战
前言
关于 LeetCode 数组类型题目的相关解法,可见LeetCode 数组类型题目做前必看,分类别解法总结了题目,可以用来单项提高。觉得有帮助的话,记得多多点赞关注哦,感谢!
题目描述
给你一个非负整数数组 nums ,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
假设你总是可以到达数组的最后一个位置。
示例 1:
输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
示例 2:
输入: nums = [2,3,0,1,4]
输出: 2
链接:leetcode-cn.com/problems/ju…
题解
这道题目主要应用的是贪心算法, 什么是贪心算法呢? 一般将求解过程分成若干个步骤,但每个步骤都应用贪心原则,选取当前状态下最好/最优的选择(局部最有利的选择),并以此希望最后堆叠出的结果也是最好/最优的解。简单来讲就是说每次都用最小代价来将问题的范围减小.本题是典型的贪心算法.可以在下面的解释中体会一下贪心算法.
- 逆向贪心算法. 其实就是我们从后向前考虑, 假设已经跳到最后一个位置上了, 那么贪心算法就是找出能跳到最后一个位置上且离最后一个位置最远的元素. 找到这个元素之后, 问题就缩小为如何用最小跳跃次数跳跃到当前这个元素的位置. 因为每次我们都找到最远跳到最后一个位置上的元素, 所以最后的答案一定是最优解. 这里有个点是下标 i 位置的元素能达到的最远的位置为 i + nums[i]. 具体代码如下.
时间复杂度 O(n²), 空间复杂度 O(1)
/**
* @param {number[]} nums
* @return {number}
*/
var jump = function(nums) {
const n = nums.length
let last_position = n - 1
let ans = 0
while (last_position > 0) {
// 找到最远的能够到达最后一个位置的元素
for (let i = 0; i < n; ++i) {
if (i + nums[i] >= last_position) {
last_position = i
++ans
break
}
}
}
return ans
};
- 正向贪心. 在方法1中, 我们每次都要通过一个循环来找出最远能够到达最后一个位置的元素, 然后用这个元素的下标来更新 last_position. 其实, 我们可以通过正向走, 目标时在同样的步数中走最远的路, 也是这个问题的最优解. 怎么做到呢? 定义两个变量, currLastPosition, maxLastPosition, 其中, currLastPosition 表示我们在当前步数下能够到达的最远的下标, maxLastPosition 表示我们在步数加1的情况下能够到达的最远的下标. 我们只需要在当前步数能够到达的下标范围中找出最大的 maxLastPosition, 在当前步数走到了 currLastPosition 的位置时, 步数加1, 把 maxLastPosition 的值赋予 currLastPosition, 再重新逐步更新 maxLastPosition 的值, 直到最后一个位置. 这就是整个过程, 核心思想就是如何在相同的步数下走最远的距离. 具体算法见代码. 时间复杂度为 O(n), 空间复杂度为 O(1)
/**
* @param {number[]} nums
* @return {number}
*/
var jump = function(nums) {
let i = 0
let lastMaxPosition = 0
let maxPosition = 0
let step = 0
for (let i = 0; i < nums.length - 1; i++) {
// 更新下一步中最远能达到的距离
maxPosition = Math.max(maxPosition, i + nums[i])
// 达到了当前步数最远的位置
if (i === lastMaxPosition) {
step ++
lastMaxPosition = maxPosition
}
}
return step
};