英文:
Algorithm Full-DFS output not as expected
问题
The result [4, 2, 3, 1, 0] is because of the order in which the Depth-First Search (DFS) algorithm explores the nodes in your adjacency list. DFS starts at node 0 and explores its adjacent nodes, which are 1. Then it goes to node 1 and explores its adjacent nodes [2, 3], and so on.
So, the order of exploration follows the order of the adjacency list. In your case, it's [4, 2, 3, 1, 0], which is entirely valid based on how DFS works.
Whether this result is meaningful or not depends on the specific problem you are trying to solve with DFS and the structure of your graph. It's a valid DFS traversal order, but whether it's meaningful for your application depends on the context.
英文:
I copied Python code DFS and full-DFS from 6.006 and this code make me confused, its result not run as expected, please help me, thank you
def dfs(Adj, s, parent = None, order = None):
if parent is None:
parent = [None for v in Adj]
parent展开收缩 = s
order = []
print(f's:{s}')
print(f'Adj展开收缩:{Adj展开收缩}')
for v in Adj展开收缩:
if parent[v] is None:
print(f'parent[{v}] is None:{parent[v]}')
parent[v] = s
dfs(Adj, v, parent, order)
order.append(s)
print(f'dfs order:{order}')
print(f'dfs parent:{order}')
return parent, order
def full_dfs(Adj):
parent = [None for v in Adj]
order = []
print(f'full_dfs:{parent}')
print(f'full_dfs:{len(Adj)}')
print(f'full_dfs:{range(len(Adj))}')
for v in range(len(Adj)):
print(f'{v}')
if parent[v] is None:
parent[v] = v
dfs(Adj, v, parent, order)
return parent, order
adj = [[1],[2,3],[4],[4],[]]
par,order = full_dfs(adj)
print(f'{par}')
print(f'{order}')
#
#*******************Output***************************
#full_dfs:[None, None, None, None, None]
#full_dfs:5
#full_dfs:range(0, 5)
#0
#s:0
#Adj展开收缩:[1]
#parent[1] is None:None
#s:1
#Adj展开收缩:[2, 3]
#parent[2] is None:None
#s:2
#Adj展开收缩:[4]
#parent[4] is None:None
#s:4
#Adj展开收缩:[]
#dfs order:[4]
#dfs parent:[4]
#dfs order:[4, 2]
#dfs parent:[4, 2]
#parent[3] is None:None
#s:3
#Adj展开收缩:[4]
#dfs order:[4, 2, 3]
#dfs parent:[4, 2, 3]
#dfs order:[4, 2, 3, 1]
#dfs parent:[4, 2, 3, 1]
#dfs order:[4, 2, 3, 1, 0]
#dfs parent:[4, 2, 3, 1, 0]
#1
#2
#3
#4
#[0, 0, 1, 1, 2]
#[4, 2, 3, 1, 0]
#
#
#** Process exited - Return Code: 0 **
#Press Enter to exit terminal
My question is, why its result is [4, 2, 3, 1, 0] rather than [4,2,1,0] or [4,3,1,0], because Depth first right?
In my opinion, [4, 2, 3, 1, 0] meaningless, do you agree?
答案1
得分: 2
首先,您的代码访问所有节点。 "深度优先" 不意味着 "仅首个"。它意味着在查看兄弟节点之前,它会首先查看子节点。但它的目的是访问所有节点。
您的代码会访问所有节点,因为 parent
记录了哪些节点已经被访问,哪些尚未被访问,如果它们尚未被访问,那么 full_dfs
和 dfs
中的循环会确保它们被访问。当一个节点被传递给 dfs
时,它最终会被附加到 order
,因此预期所有节点最终都会出现在该列表中。
其次,只有当从该节点可达的所有其他节点都已被访问(通过递归)时,节点才会被添加到 order
。这就是所谓的后序遍历,因为对节点的操作在其所有后继/子节点上执行之后才执行。
现在来看示例图:
dfs
递归调用从节点 0 到 1 到 2 到 4。在那个阶段,没有更多的后继节点了,所以 4 被附加到 order
,并且最深的 dfs
调用返回到 dfs
的执行上下文,其中当前节点是 2。同样,在那里我们发现没有其他后继节点,因此 2 被附加到 order
,并且该 dfs
调用也返回。我们回到了当前节点是 1 的 dfs
的执行上下文。
在这里,我们有另一个后继节点(循环的第二次迭代),因此会进行另一个递归调用,其中 3 是当前节点。尽管此节点有一个后继节点(4),但该节点已经被访问过(其 parent
已经设置),因此 3 被附加到 order
,并且执行返回到当前节点为 1 的执行上下文。
对于节点 1,没有其他后继节点,因此将 1 附加到 order
,并且 dfs
返回到 dfs
的原始执行上下文,其中当前节点是 0。此节点没有其他后继节点,因此将 0 附加到 order
。
我们可以将这个过程可视化一下,其中每个 "盒子" 表示函数的执行上下文。执行从上到下进行。当函数返回时,其执行上下文结束(盒子关闭),执行在周围的 "盒子" 中继续:
希望这澄清了该过程,以及为什么在从 0 开始的情况下,您会得到 [4, 2, 3, 1, 0] 作为输出:这是您的图的后序深度优先遍历。
英文:
First of all, your code visits all nodes. "depth-first" does not mean "first-only". It means that before looking to sibling nodes, it will first look to children nodes. But its purpose is to visit all nodes.
All nodes are visited by your code because parent
records which nodes are visited, and which not yet, and if they are not yet visited, the loops in both full_dfs
and dfs
ensure they are. And when a node is given to dfs
, it eventually gets appended to order
, so it is expected that all nodes end up in that list.
Secondly, a node is only added to order
when all other nodes that are reachable from that node have been visited (through recursion). This is what is called a post-order traversal, since the action on the node is performed after the same action has been done on all its successors/children.
Now to the example graph:
dfs
recursive calls are made to go from node 0 to 1 to 2 to 4. At that stage there are no more successor nodes, so 4 is appended to order
, and that deepest dfs
call returns to get back to the execution context of dfs
where the current node is 2. Also there we find there are no other successors, so 2 is appended to order
and also that dfs
call returns. We get back to the execution context of dfs
where the current node is 1.
Here we have another successor (second iteration of the loop), and so another recursive call is made where 3 is the current node. Although this node has a successor (4), that node has already been visited (its parent
has been set), so 3 is appended to order
and execution returns to the execution context where 1 is the current node.
For node 1 there are no other successors, so 1 is appended to order
and dfs
returns to the original execution context of dfs
where the current node is 0. This node has no other successors, so 0 is appended to order
.
We can visualise this process a bit where each "box" represents an execution context of a function. Execution goes from top to bottom. When a function returns, its execution context ends (the box closes) and execution continues in the surrounding "box":
+-- full_dfs() --------------------------------+
| parent[0] = 0 |
| +-- dfs(s=0) ----------------------------+ |
| | parent[1] = 0 | |
| | +-- dfs(s=1) ----------------------+ | |
| | | parent[2] = 1 | | |
| | | +-- dfs(s=2) ----------------+ | | |
| | | | parent[4] = 2 | | | |
| | | | +-- dfs(s=4) ----------+ | | | |
| | | | | order.append(4) | | | | |
| | | | +----------------------+ | | | |
| | | | order.append(2) | | | |
| | | +----------------------------+ | | |
| | | parent[3] = 1 | | |
| | | +-- dfs(s=3) ----------------+ | | |
| | | | order.append(3) | | | |
| | | +----------------------------+ | | |
| | | order.append(1) | | |
| | +----------------------------------+ | |
| | order.append(0) | |
| +----------------------------------------+ |
+----------------------------------------------+
Hope this clarifies the process and why you get [4, 2, 3, 1, 0] as output: it is the post-order DFS traversal for your graph when starting at 0.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论