解法一:贪心策略
贪心选择性质就是说能够通过局部最优解直接推导出全局最优解。
题意可以理解为:请问通过题目中的跳跃规则,最多能跳多远?如果能够越过最后一格,返回 true,否则返回 false。
只需要遍历所有元素,记录当前能到达的最远位置,并判断能否越过最后一格就行了。具体步骤:
- 从左到右遍历 nums,同时维护当前能跳到的最远位置fastest,初始值为 0。
- 如果发现某个下标 i > fastest,说明跳不到当前下标位置,返回false
- 否则,比较fastest和 i + nums[i]的大小,取较大者更新fastest
- 如果最终循环结束,fastest越过了最后一个下标位置,返回true,否则返回false。
func canJump(nums []int) bool {
fastest := 0
for i, jump := range nums{
if i > fastest{ // 无法到达下标 i
return false
}
fastest = max(fastest, i+jump)
}
return fastest >= len(nums)-1
}
func max(a, b int) int{
if a > b{
return a
}
return b
}
另一种写法
func canJump(nums []int) bool {
farthest := 0
for i := 0; i<len(nums)-1; i++{ // 总共能跳的次数是len-1
// 不断更新能跳到的最远位置
farthest = max(farthest, i + nums[i])
// 中间可能碰到0,卡住了,那就不能再继续往后跳了,肯定不能到达最后一格
if farthest == i{
return false
}
}
// 遍历完,判断farthest能否越过最后一格
return farthest >= len(nums)-1
}
func max(a, b int) int{
if a > b{
return a
}
return b
}
- 时间复杂度:O(N)
- 空间复杂度:O(1)
跳跃游戏 II
来看这道题的进阶版,leetcode.cn/problems/ju…
现在题意变成是:保证你一定可以跳到最后一格,请问你最少要跳多少次,才能跳过去?
思路一:贪心策略
不需要真的递归穷举出所有选择的具体结果来比较求最值,而只需要每次选择那个最有潜力的局部最优解,最终就能得到全局最优解
func jump(nums []int) int {
farthest := 0 // 当前能够跳到的最远距离
end := 0 // [i...end]表示可以选择的跳跃步数,end为可跳跃区间的右边界
steps := 0 // 记录总的跳跃步数
for i := 0; i<len(nums)-1; i++{
farthest = max(farthest, i + nums[i])
if end == i{ // 已经到达了当前可跳区间的右边界,需要跳一步,扩大一下右边界
steps++
end = farthest // 直接跳到当前能够到的最远位置
}
}
return steps
}
func max(a, b int) int{
if a > b {
return a
}
return b
}
思路二:抽象为建桥问题
我们换个思路分析这道题
显然,下一步我们应该选择右端点最大的那座桥,因为它能让你走得更远,从而保证我们最终需要修建的桥数更少
func jump(nums []int) int {
curRight := 0 // 已建造桥的右端点
nextRight := 0 // 可选择的下一座桥的右端点最大值
res := 0 // 修建的桥数
for i, v := range nums{
if i == len(nums) -1 { // 已经走到了终点,无需再建桥
break
}
nextRight = max(nextRight, i+v) // 遍历过程中更新可修建桥的最大右端点
if i == curRight{ // 走到当前桥的尽头,需要修建下一座桥,选择右端点最大的
curRight = nextRight // 建桥后,右端点最远更新为nextRight
res++
}
}
return res
}
func max(a, b int) int{
if a > b {
return a
}
return b
}