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)
View Code

法二: 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)
View Code

法三: 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
View Code

ttt

posted on 2019-12-30 14:07  吃我一枪  阅读(268)  评论(0编辑  收藏  举报

导航