【问题标题】:How to backtrack when using a iterative DFS implemented via a stack使用通过堆栈实现的迭代 DFS 时如何回溯
【发布时间】:2020-09-30 11:23:59
【问题描述】:

我正在完成这个 Leetcode 问题:https://leetcode.com/problems/word-search/,我随机选择使用 while 循环和堆栈迭代地实现 DFS,但是在回溯时遇到了一些不便,如果我以递归方式完成问题,我通常不会发生这种情况即我只能考虑实现一个列表 (visited_index) 来跟踪我访问过的索引并弹出值以在回溯时将布尔矩阵 visited 设置回 False

from collections import defaultdict
class Solution:
    def exist(self, board: List[List[str]], word: str) -> bool:
        starting_points = defaultdict(list)
        m, n = len(board), len(board[0])
        for i in range(m):
            for j in range(n):
                starting_points[board[i][j]].append((i,j))
        
        
        start = starting_points[word[0]]
        visited = [[False] * n for _ in range(m)]
        stack = []
        directions = [(1,0), (0,-1), (-1,0), (0,1)]
        
        for s in start:
            stack.append((s[0], s[1], 0))
            visited_index = [] # EXTRA LIST USED
            while stack:
                x, y, count = stack.pop()
                while len(visited_index) > count:
                    i, j = visited_index.pop()
                    visited[i][j] = False # SETTING BACK TO FALSE WHEN BACKTRACKING
                if x < 0 or x >= m or y < 0 or y >= n or visited[x][y] or board[x][y] != word[count]:
                    continue
                else:
                    
                    visited[x][y] = True
                    visited_index.append((x,y))
                    if count + 1 == len(word):
                        return True
                    for d in directions:
                        i, j = x + d[0], y + d[1]
                        stack.append((i,j, count + 1))
            
            else:
                stack.clear()
                for i in range(m):
                    for j in range(n):
                        visited[i][j] = False
        return False

我相信,在递归方法中,我可以在函数末尾将visited 布尔值重置为False,而无需使用额外的列表。在使用堆栈进行迭代 DFS 时,是否有人建议不要引入额外的数据结构?

【问题讨论】:

    标签: python algorithm stack depth-first-search


    【解决方案1】:

    只要有子节点正在处理,我就会将父节点保留在堆栈中。然后,当所有子级都已处理完毕并且您将父级从堆栈中弹出时,您将有合适的时间来删除该父级的已访问标记。

    实现该想法的一种方法是在您放入堆栈的元组中再添加一个信息:最后一个方向。您可以使用该信息查找下一个方向,如果有可用的有效方向,则使用该新方向将当前节点推回堆栈,然后将相应的子节点推入堆栈。后者为“前一个”方向指示获得一些默认值。例如 -1。

    我修改了你的代码以符合这个想法:

    class Solution:
        def exist(self, board: List[List[str]], word: str) -> bool:
            stack = []
            m, n = len(board), len(board[0])
            for i in range(m):
                for j in range(n):
                    if board[i][j] == word[0]:
                        # 4th member of tuple is previous direction taken. -1 is none.
                        stack.append((i, j, 1, -1))  # count=1, side=-1
                    
            visited = [[False] * n for _ in range(m)]
            directions = [(1,0), (0,-1), (-1,0), (0,1)]
    
            while stack:
                x, y, count, side = stack.pop()
                # perform the success-check here, so it also works for 1-letter words.
                if count == len(word):
                    return True
                visited[x][y] = True  # will already be True when side > -1
                # find next valid direction
                found = False
                while side < 3 and not found:
                    side += 1
                    dx, dy = directions[side]
                    i, j = x + dx, y + dy
                    found = 0 <= i < m and 0 <= j < n and not visited[i][j] and board[i][j] == word[count]
                if not found:  # all directions processed => backtrack
                    visited[x][y] = False
                    continue
                stack.append((x, y, count, side))  # put node back on stack
                stack.append((i, j, count + 1, -1))
            return False
    

    【讨论】:

      猜你喜欢
      • 2011-08-03
      • 2016-03-07
      • 1970-01-01
      • 2016-04-16
      • 1970-01-01
      • 2017-07-27
      • 2021-10-11
      • 2015-05-17
      • 1970-01-01
      相关资源
      最近更新 更多