Prim 算法生成的最小生成树的预序树遍历
Preorder tree walk of a minimum spanning tree generated by Prim's algorithm
我正在尝试实现一种近似算法来解决旅行商问题 (TSP),当三角不等式对边权重成立时可以使用该算法。如 Cormen 等人算法简介(第 3 3d.)所述,伪代码为:
这是一个例子:
我正在纠结的是如何在 Prim 算法生成的树上实现前序树遍历。由于这不是二叉搜索树,https://en.wikipedia.org/wiki/Tree_traversal#Pre-order_2 处给出的伪代码似乎不适用。
相反,节点没有 left
和 right
属性,而是具有 key
和 parent
属性。以下是它们在我的 Prim 算法实现中的生成方式(带有一个小测试用例):
import math
import copy
import pytest
import pandas as pd
from cached_property import cached_property
class Node(object):
def __init__(self, key=math.inf, parent=None):
self.key = key
self.parent = parent
def __lt__(self, other):
return self.key < other.key
class Graph(object):
def __init__(self, edges):
self.edges = edges
@cached_property
def nodes(self):
_nodes = set()
for edge in self.edges:
_nodes.add(edge[0])
_nodes.add(edge[1])
return {node: Node() for node in list(_nodes)}
@cached_property
def adj(self):
A = {node: [] for node in self.nodes}
for edge in self.edges:
u, v, _ = edge
A[u].append(v)
A[v].append(u)
return A
@cached_property
def w(self):
N = len(self.nodes)
none_array = [[None for _ in range(N)] for _ in range(N)]
df = pd.DataFrame(none_array, index=sorted(self.nodes), columns=sorted(self.nodes))
for edge in self.edges:
u, v, weight = edge
df.set_value(u, v, weight)
df.set_value(v, u, weight)
return df
def mst_prim(self, root):
r = self.nodes[root]
r.key = 0
Q = copy.copy(self.nodes)
while Q:
u = min(Q, key=Q.get)
u_node = Q.pop(u)
for v in self.adj[u]:
if v in Q and self.w[u][v] < self.nodes[v].key:
self.nodes[v].parent = u
self.nodes[v].key = self.w[u][v]
@pytest.fixture
def edges_simple():
return [('a', 'b', 4),
('a', 'h', 8),
('b', 'h', 11),
('h', 'i', 7),
('b', 'c', 8),
('h', 'g', 1),
('i', 'c', 2),
('i', 'g', 6),
('c', 'd', 7),
('g', 'f', 2),
('c', 'f', 4),
('d', 'f', 14),
('d', 'e', 9),
('f', 'e', 10)
]
def test_mst_prim(edges_simple):
graph = Graph(edges_simple)
graph.mst_prim(root='a')
# print("\n")
# for u, node in graph.nodes.items():
# print(u, node.__dict__)
assert graph.nodes['a'].parent is None
assert graph.nodes['i'].parent == 'c'
assert graph.nodes['d'].parent == 'c'
if __name__ == "__main__":
# pytest.main([__file__+"::test_mst_prim", "-s"])
pytest.main([__file__, "-s"])
如何在此图上执行前序树遍历? (请注意,这个问题类似于pre-order traversal of a Minimum spanning tree,但我发现那里给出的答案相当高)。
我建议您在 Node
class 中添加一个名为 children
的新列表。
在您的 Prim's
算法之后,您可以 运行 通过您获得的节点并将它们添加到它们的 parent 的 children
。复杂度是 O(n)
,所以没什么大不了的。之后 DFS
遍历就很容易了。
但是,正如您提到的 post 中一样,您必须为 children 选择一个顺序进行 preorder
遍历。在您的情况下,当您仅参考 parent
时,无法知道 left-most
child 是什么。
我有点疑惑为什么Cormen书里的算法会提到前序遍历,因为最小生成树MST中的一个节点"children"之间是没有序的
据我了解,您只需按照上述 here and here 在 MST 上执行深度优先搜索 (DFS)。这意味着如果在一个节点 u
上,您将访问它的邻居之一或 "children",没有特定的顺序。
您可以通过在 class.
中使用表示为 adj
的图的邻接表表示来实现 DFS
我正在尝试实现一种近似算法来解决旅行商问题 (TSP),当三角不等式对边权重成立时可以使用该算法。如 Cormen 等人算法简介(第 3 3d.)所述,伪代码为:
这是一个例子:
我正在纠结的是如何在 Prim 算法生成的树上实现前序树遍历。由于这不是二叉搜索树,https://en.wikipedia.org/wiki/Tree_traversal#Pre-order_2 处给出的伪代码似乎不适用。
相反,节点没有 left
和 right
属性,而是具有 key
和 parent
属性。以下是它们在我的 Prim 算法实现中的生成方式(带有一个小测试用例):
import math
import copy
import pytest
import pandas as pd
from cached_property import cached_property
class Node(object):
def __init__(self, key=math.inf, parent=None):
self.key = key
self.parent = parent
def __lt__(self, other):
return self.key < other.key
class Graph(object):
def __init__(self, edges):
self.edges = edges
@cached_property
def nodes(self):
_nodes = set()
for edge in self.edges:
_nodes.add(edge[0])
_nodes.add(edge[1])
return {node: Node() for node in list(_nodes)}
@cached_property
def adj(self):
A = {node: [] for node in self.nodes}
for edge in self.edges:
u, v, _ = edge
A[u].append(v)
A[v].append(u)
return A
@cached_property
def w(self):
N = len(self.nodes)
none_array = [[None for _ in range(N)] for _ in range(N)]
df = pd.DataFrame(none_array, index=sorted(self.nodes), columns=sorted(self.nodes))
for edge in self.edges:
u, v, weight = edge
df.set_value(u, v, weight)
df.set_value(v, u, weight)
return df
def mst_prim(self, root):
r = self.nodes[root]
r.key = 0
Q = copy.copy(self.nodes)
while Q:
u = min(Q, key=Q.get)
u_node = Q.pop(u)
for v in self.adj[u]:
if v in Q and self.w[u][v] < self.nodes[v].key:
self.nodes[v].parent = u
self.nodes[v].key = self.w[u][v]
@pytest.fixture
def edges_simple():
return [('a', 'b', 4),
('a', 'h', 8),
('b', 'h', 11),
('h', 'i', 7),
('b', 'c', 8),
('h', 'g', 1),
('i', 'c', 2),
('i', 'g', 6),
('c', 'd', 7),
('g', 'f', 2),
('c', 'f', 4),
('d', 'f', 14),
('d', 'e', 9),
('f', 'e', 10)
]
def test_mst_prim(edges_simple):
graph = Graph(edges_simple)
graph.mst_prim(root='a')
# print("\n")
# for u, node in graph.nodes.items():
# print(u, node.__dict__)
assert graph.nodes['a'].parent is None
assert graph.nodes['i'].parent == 'c'
assert graph.nodes['d'].parent == 'c'
if __name__ == "__main__":
# pytest.main([__file__+"::test_mst_prim", "-s"])
pytest.main([__file__, "-s"])
如何在此图上执行前序树遍历? (请注意,这个问题类似于pre-order traversal of a Minimum spanning tree,但我发现那里给出的答案相当高)。
我建议您在 Node
class 中添加一个名为 children
的新列表。
在您的 Prim's
算法之后,您可以 运行 通过您获得的节点并将它们添加到它们的 parent 的 children
。复杂度是 O(n)
,所以没什么大不了的。之后 DFS
遍历就很容易了。
但是,正如您提到的 post 中一样,您必须为 children 选择一个顺序进行 preorder
遍历。在您的情况下,当您仅参考 parent
时,无法知道 left-most
child 是什么。
我有点疑惑为什么Cormen书里的算法会提到前序遍历,因为最小生成树MST中的一个节点"children"之间是没有序的
据我了解,您只需按照上述 here and here 在 MST 上执行深度优先搜索 (DFS)。这意味着如果在一个节点 u
上,您将访问它的邻居之一或 "children",没有特定的顺序。
您可以通过在 class.
中使用表示为adj
的图的邻接表表示来实现 DFS