微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用通过堆栈实现的迭代DFS时如何回溯

如何解决使用通过堆栈实现的迭代DFS时如何回溯

我正在完成这个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,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时不引入额外的数据结构?

解决方法

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

实现该想法的一种方法是将更多信息放入堆栈中的元组:最后采用的方向。您可以使用该信息来查找要采取的下一个方向,如果有可用的有效方向,请使用该新方向将当前节点推回堆栈中,然后将相应的子节点推入堆栈中。后者获得该“先前”方向指示的一些默认值。例如-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,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,side))  # put node back on stack
            stack.append((i,count + 1,-1))
        return False
,
from typing import List
class Solution:
    def exist(self,board:List[List[str]],word:str)->bool:
        ROWS,COLS=len(board),len(board[0])
        # we cannot revisit the same character twice
        path=set()
        # i is the current char in the word that we are looking for
        def dfs(r,c,i):
            if i==len(word):
                return True
            if r<0 or c<0 or r>=ROWS or c>=COLS or word[i]!=board[r][c] or (r,c) in path:
                return False
            path.add((r,c))
            res=(dfs(r+1,i+1) or
                dfs(r-1,i+1) or
                dfs(r,c+1,c-1,i+1))
            # after finishing each 
            path.remove((r,c))
            return res

        for r in range(ROWS):
            for c in range(COLS):
                if dfs(r,0): return True
        return False

enter image description here

假设我们的目标是“ABCCED”。我们从调用 dfs(0,0) 开始。在第二个 if 语句中,word[i]!=board[r][c] 将确保黑板上的字母与 word[index] 相同。所以在棋盘上,board(0,0) 与 word[0] 相同,所以我们可以继续执行函数,即

path.add(r,c)

现在在调用堆栈上我们有 dfs(0,0),因为它返回 true,我们可以调用其中之一

            (dfs(r+1,i+1) or
            dfs(r-1,i+1) or
            dfs(r,i+1))

这意味着对于每个字母检查,我们调用 dfs 4 次,直到我们得到 True。在 dfs(0,0) 之后,我们调用 dfs(0,1) 。接下来是 dfs(0,2,2)dfs(1,3)dfs(2,4)dfs(2,5)。这个函数调用会在调用栈上,直到

     if i==len(word):
        return True

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。