题目

题意
给你一个非负整数的数组nums,然后数组中的元素值代表你能从该元素位置最多往后走多少个格子,最终目的是让你判断,这个非负整数数组,能不能满足你从第一个位置走到最后一个位置。
思路
这道题我觉得可以理解为是贪心。
为了能够更直观的讲清楚这道题,我们把这个非负整数数组看作一排格子;然后判断能够到达数组末端的这个行为,我们把其看作是雷达扫描;每个索引对应的非负整数数组的值(格子中的值),看作是雷达在此处可以扩展的扫描长度。
总体思路就是,我们从左边界开始,不断地向右侧尝试扫描格子。但是再扫描之前,我们必须要考虑当前雷达的最远扫描边界是多少,不可以大于这个半径长度(下文会详细解释为什么)。(1)
具体而言,一开始雷达初始半径为0,并且我们站在0号格子上,然后我们要看0号格子最多能给雷达加多少扫描长度,与此同时,我们记录当下雷达的扫描边界(此时就是0号格子对应的数值)。有了这个边界之后,我们就开始向右推进,准备扫描下一个格子。
在上述过程中,当我们向右走到下一个格子时,这个格子也会提供一个扫描半径,但是有一个问题,就是这个新格子能够提供的扫描边界未必会超过目前已有的最大的扫描边界。因此,为了确保我们之前探索到的最大扫描边界不被覆盖,我们必须在新格子能够到达的扫描边界和之前探索到的最大的扫描边界之间,去取一个最大值。这就意味着,我们雷达的扫描边界是单调递增的。
此外,在向右推进的过程中,并不是盲目一直向后遍历,是有右边界的,这里会解答(1)处的问题。如果在某一处,我们尝试去扫描的格子,他的位置已经超出了雷达当前的最远扫描边界,那就说明,之前的格子没有足够的半径值去支撑我们走到这个位置,也就是说路径出现了断层,因此断层往后的所有格子我们都到达不了,就没必要继续往后探了,直接return false就好。
那么,什么时候能够知道自己可以扫到最右侧的格子呢?
就是在向右推进的过程中,只要我们更新的扫描边界>=数组的最后一个索引,这就意味着,可以从第一个位置跳到最末尾。
所以这道题贪心的本质,并不是说我每次非要把当前格子能提供的边界全用掉,而是说,我们要在能提供的边界的范围里面,去不断地吸收能量最大的格子,贪心地扩展我们的右边界,只要右边界扩展的越多,并且能摸到终点,就一定能够到达。
图示
(a)
由上图(a)所示,就是一开始,我们从索引0开始,索引0能在当前位置提供两个扫描半径,所以半径就是i + nums[i],即0 + 2 = 2,因此扫描右边界就到了索引2处。然后我们在[0,2]这个数组索引区间内往右遍历,下一个就是索引1,索引1在当前位置能提供3个半径,那么最终的扫描边界就是1+nums[1],即1 + 3 = 4,且4大于之前的2,所以最大边界更新为4。此时就可以发现,现在能够到达的扫描边界正好能够碰到数组末端,也就是说明,我们可以从数组开头,通过中间的元素跳到末尾。
(b)
图(b)就是结果为false的例子。
一开始我们仍是从索引0开始,在索引0我们最远能够到达索引3的位置(红线);然后我们暂且以红线为右边界,即3个半径长度,然后继续在[0,3]中向右遍历,然后我们会发现,不论是索引1,2还是3,它们所能到达的最远右边界就是索引3这个位置,仍无法到达末尾索引4的位置。此时呢,你若是再往右找的话,就来到了图中的粉色圆点,这也就是前文(1)处我们控制右边界的那种情况。就是前面最远的才能到索引3,但你你粉色圆点却到了索引4的位置,这是无论无何都到达不了的位置,所以也就是出现了断层,索引4及其之后的所有位置,我们都到达不了,返回false即可。
代码
class Solution:
def canJump(self, nums: list[int]) -> bool:
n = len(nums)
flag = 0 # 当前能到达的最远位置
if n == 1 :
return True
for i in range(n) :
if i > flag :
return False
flag = max(flag, nums[i] + i)
if flag >= n - 1 :
return True
return False
转载自 CSDN-专业IT技术社区



