问题描述
我正在完成这个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
假设我们的目标是“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