问题描述
我正在实现深度优先搜索算法。有两个要求:我必须使用堆栈(无递归),并且它还应该返回发现时间和完成时间。这是我实现的使用递归的代码:
def dfs(graph,source):
"""Depth-first search algorithm
This function computes depth-first search and prints the nodes as it travels
Arguments:
graph: List[List[int]],adjacency matrix of the graph
source: int,the starting point
Returns:
None
"""
visited = [False] * len(graph)
time_start = [0] * len(graph) # Time when vertex is discovered
time_finish = [0] * len(graph) # Time when vertex has finished discovering
time = 0
dfs_visit(graph,source,visited,time,time_start,time_finish)
return time_start,time_finish
def dfs_visit(graph,time_finish):
time += 1
time_start[source] = time
visited[source] = True
print(source,end = " ")
for i,val in enumerate(graph[source]):
if not visited[i] and val != 0:
dfs_visit(graph,i,time_finish)
time += 1
time_finish[source] = time
样本输入:
graph = [[0,1,0],[1,1],[0,1]]
预期输出:2 0 1 3 ([2,3,2],[3,4,2,3])
,其中第一个数组指示发现时间,第二个数组指示完成时间(按索引)。
有了这个想法,我就使用Stack实现了DFS:
def dfs_stack(graph,source):
"""Depth-first search algorithm using stack
This function computes depth-first search and prints the nodes as it travels
Arguments:
graph: List[List[int]],the starting point
Returns:
None
"""
visited = [False] * len(graph)
dfs_visit(graph,visited)
return time_start,visited):
stack = []
stack.append(source)
while (len(stack)):
s = stack[-1]
stack.pop()
if not visited[s]:
print(s,end = " ")
visited[s] = True
for idx,val in enumerate(graph[s]):
if (not visited[idx]) and val != 0:
stack.append(idx)
我尝试用time += 1; time_start[s] = ...
来计算这些时间,但它输出的结果很奇怪。我应该在哪里正确放置时间计数器?
解决方法
关于您的代码的几句话:
关于递归解决方案
记录的时间有些混乱,因为您有重复的时间戳记(例如3)。这是因为您对time
进行的递增不会反馈给调用方,后者具有自己的time
变量实例。我会将time
设为非局部变量,以使其不断增加。
所以我将其更改为:
def dfs(graph,source):
def dfs_visit(graph,source,visited,time_start,time_finish):
nonlocal time
time += 1
time_start[source] = time
visited[source] = True
print(source,end = " ")
for i,val in enumerate(graph[source]):
if not visited[i] and val != 0:
dfs_visit(graph,i,time_finish)
time += 1
time_finish[source] = time
visited = [False] * len(graph)
time_start = [0] * len(graph)
time_finish = [0] * len(graph)
time = 0
dfs_visit(graph,time_finish)
return time_start,time_finish
现在print(dfs(graph,2))
的输出将是:
2 0 1 3 ([2,3,1,6],[5,4,8,7])
这对我来说更有意义,但也许我误解了您打算使用time
做些什么。
关于迭代解
-
s = stack[-1]
后跟stack.pop()
可以写成s = stack.pop()
-
您正在将节点的所有个子代推入堆栈,然后再处理它们的子代。这意味着,深度优先遍历实际上将从后到先访问子级,而递归实现则是从最后到最后地访问子级。
记录完成时间
现在是您问题的核心。如果要记录访问的结束时间,则需要留下堆栈上的节点的痕迹,并且仅在处理完所有子节点之后才将其从该堆栈中删除。不早。
实现该目标的一种方法是,将最后一个从节点访问的邻居存储在堆栈中。因此,您将存储(节点,邻居)元组。如果尚未从该节点访问下一个节点,则neighbor
的初始值可能为-1。
这是如何工作的:
def dfs_stack(graph,source):
visited = [False] * len(graph)
time_start = [0] * len(graph)
time_finish = [0] * len(graph)
time = 0
stack = [(source,-1)]
while stack:
node,neighbor = stack.pop()
if neighbor == -1:
if visited[node]:
continue
visited[node] = True
print(node,end = " ")
time += 1
time_start[node] = time
try:
neighbor = graph[node].index(1,neighbor + 1)
stack.append((node,neighbor))
if not visited[neighbor]:
stack.append((neighbor,-1))
except ValueError: # no more neighbors...
time += 1
time_finish[node] = time
如果我们用print(dfs_stack(graph,2))
进行调用,我们还会得到:
2 0 1 3 ([2,7])