1306跳跃游戏III
题目: 这里有一个非负整数数组arr,你最开始位于该数组的起始下标 start 处。当你位于下标 i 处时,你可以跳到 i + arr[i] 或者 i - arr[i]。请你判断自己是否能够跳到对应元素值为 0 的 任意 下标处。注意,不管是什么情况下,你都无法跳到数组之外。
来源: https://leetcode-cn.com/problems/jump-game-iii/
法一: 自己的代码 注意返回False和返回None都是正确的!因为系统内部会计算他们的布尔变量,都是False.
这个里面如果某个索引被遍历了一遍就无需再遍历了,(即便这个索引只朝右或只超左被遍历过,也无需再遍历了,因为比如某个索引只超右遍历了,但它的超左的那个索引已经被记录到队列中了,或者是树的某个分支还没有被遍历,等待遍历,所以一定不会出现漏掉的情况)
思路: 利用常规的回溯模板,类似于数独题的解法,每次回溯时,先判断以前是否遍历过,再判断索引位置值是否为0,再确定下次要回溯的索引,注意用这个模板的难点在于返回值的问题,如果所有的回溯分支都跳不到0,则返回值有两种情况,一种是False,即第一次进去超左超右都跳出去了,这时返回False,另一种是回溯的时候,返回False,这时直接pass,这时如果其它的分支也是pass的话,最后的返回值是None,这一点要明确.
from collections import defaultdict from typing import List class Solution: def canReach(self, arr: List[int], start: int) -> bool: l = len(arr) memo = defaultdict(int) def recursion(index, val): # 先判断之前是否判断过,如果判断过,直接返回False if memo[index] == 1: return False elif arr[index] == 0: return True memo[index] += 1 all = [] # 用于确定下次遍历的索引 if index + val > l-1: pass else: all.append((index+val, arr[index+val])) if index - val < 0: pass else: all.append((index-val, arr[index-val])) if len(all) == 0: return False for i,j in all: # 如果返回值为False或None,则继续判断,直到遍历完所有可以遍历的索引 a = recursion(i,j) if a is False or a is None: pass else: return a return True if recursion(index=start, val=arr[start]) else False # ans = recursion(index=start, val=arr[start]) # if ans is None or ans is False: # return False # else: # return True if __name__ == '__main__': duixiang = Solution() a = duixiang.canReach(arr = [3,0,2,1,2], start = 2) # a = duixiang.canReach(arr = [4, 2, 3, 0, 3, 1, 2], start = 0) # a = duixiang.canReach(arr = [31, 70, 40, 4, 27, 28, 44, 17, 8, 16, 64, 14, 30, 17, 25, 61, 33, 50, 45, 67, 12, 43, 72, 0, 26, 24, 33, 66, 22, # 11, 61, 15, 2, 18, 51, 37, 34, 7, 14, 29, 37, 3, 40, 3, 61, 20, 24, 22, 53, 18, 58, 56, 16, 44, 14, 5, 6, 19, 72, # 46, 49, 10, 42, 40, 46, 10, 6, 34, 17, 68, 62, 34, 33, 68], start = 10) print(a)
法二: dfs
思路: 利用递归回溯,关键是对返回值的处理非常巧妙!在return后面利用or,如果有一个为True,则返回True,否则返回False.\
from typing import List class Solution: def canReach(self, arr: List[int], start: int) -> bool: n = len(arr) def backtrack(i, flag): # 如果出界了,或者已经遍历过了,返回False if i < 0 or i >= n or flag[i] == 1: return False # 如果满足条件,返回True if arr[i] == 0: return True # 记录遍历过的索引 flag[i] = 1 # 有一个分支满足条件,则返回True,结束回溯,否则遍历完所有的. return backtrack(i + arr[i], flag) or backtrack(i - arr[i], flag) flag = [0 for i in range(n)] return backtrack(start, flag)
法三: bfs
思路: 利用队列实现,每次都将当前位置左右两边满足条件的索引放入队列,直到返回True或队列为空了,返回False.
from typing import List from collections import deque class Solution: def canReach(self, arr, start): n = len(arr) q = deque() visited = set() q.append(start) while q: # 从队列的左边取数 ind = q.popleft() # 每取一个都放入集合中. visited.add(ind) # 如果为0,直接返回 if arr[ind] == 0: return True # 遍历索引的左边和右边 for x in (ind - arr[ind], ind + arr[ind]): # 如果索引在范围内,且没有被遍历过,则加入队列 if 0 <= x < n and x not in visited: q.append(x) # 如果都没有返回True,则返回False. return False
ttt