我正在尝试使用联合查找数据结构在Python中实现Kruskal算法。我的实现是在这里开发的一个小示例上完成的,但是在更大的作业图上却存在一个小问题。您能帮我看看此实现有什么问题吗?
这是我的实现方式:
class UnionFind:
def __init__(self,val,leader):
self.val = val
self.leader = leader
def changeLeader(self,leader):
self.leader = leader
def returnLeader(self):
return self.leader
from collections import defaultdict
def kruskal(graph,edges, N):
T = dict()
sizes = defaultdict(lambda: 0)
edgeWeights = []
for indx, edge in enumerate(edges):
n1 = graph[edge][0]
n2 = graph[edge][1]
# print("edge is", edge,"nodes",n1.val,n2.val,"leaders",n1.leader,n2.leader)
# print("state of dict is", T.keys())
if (n1.leader == n2.leader):
# print("both nodes part of",n1.leader,'do nothing \n')
pass
elif (n1.leader in T.keys()) and (n2.leader not in T.keys()):
# print("adding ",n2.val, "to group",n1.leader,'\n')
n2.changeLeader(n1.leader)
T[n1.leader].append(n2)
sizes[n1.leader] += 1
edgeWeights.append(edge)
elif (n2.leader in T.keys()) and (n1.leader not in T.keys()):
# print("adding ",n1.val, "to group",n2.leader,'\n')
n1.changeLeader(n2.leader)
T[n2.leader].append(n1)
sizes[n2.leader] += 1
edgeWeights.append(edge)
elif (n1.leader in T.keys()) and (n2.leader in T.keys()) and (n1.leader != n2.leader):
# print("merging groups",n1.leader,n2.leader)
size1 = sizes[n1.leader]
size2 = sizes[n2.leader]
edgeWeights.append(edge)
# print("sizes are",size1, size2)
if size1 >= size2:
for node in T[n2.leader]:
if node is not n2:
node.changeLeader(n1.leader)
T[n1.leader].append(node)
sizes[n1.leader] += 1
sizes[n2.leader] -= 1
del T[n2.leader]
sizes[n2.leader] = 0
n2.changeLeader(n1.leader)
T[n1.leader].append(n2)
# print("updated list of nodes",T.keys())
# for node in T[n1.leader]:
# print("includes",node.val)
else:
for node in T[n1.leader]:
if node is not n1:
node.changeLeader(n2.leader)
T[n2.leader].append(node)
sizes[n2.leader] += 1
sizes[n1.leader] -= 1
del T[n1.leader]
sizes[n1.leader] = 0
n1.changeLeader(n2.leader)
T[n2.leader].append(n1)
else:
# print("adding new group",n1.val,n2.val,'\n')
n2.changeLeader(n1.leader)
T[n1.leader] = [n1,n2]
sizes[n1.leader] +=2
edgeWeights.append(edge)
# print("updated nodes",graph[edge][0].val,graph[edge][1].val,"leaders",
# graph[edge][0].leader,graph[edge][1].leader,"\n")
return T, edgeWeights
这是测试代码:
nodes = [UnionFind("A","A"),UnionFind("B","B"),UnionFind("C","C"),UnionFind("D","D"),UnionFind("E","E")]
graph = {1:[nodes[0],nodes[1]],2:[nodes[3],nodes[4]],
3:[nodes[0],nodes[4]],4:[nodes[0],nodes[3]],
5:[nodes[0],nodes[2]],6:[nodes[2], nodes[4]],
7:[nodes[1],nodes[2]]}
N = 5
edges = list(graph.keys())
edges.sort()
T, weight = kruskal(graph,edges,N)
for node in T['A']:
print(node.val)
print("edges",weight)
以及结果输出:
edge is 1 nodes A B leaders A B
state of dict is dict_keys([])
adding new group A B
updated nodes A B leaders A A
edge is 2 nodes D E leaders D E
state of dict is dict_keys(['A'])
adding new group D E
updated nodes D E leaders D D
edge is 3 nodes A E leaders A D
state of dict is dict_keys(['A', 'D'])
merging groups A D
sizes are 2 2
updated list of nodes dict_keys(['A'])
includes A
includes B
includes D
includes E
updated nodes A E leaders A A
edge is 4 nodes A D leaders A A
state of dict is dict_keys(['A'])
both nodes part of A do nothing
updated nodes A D leaders A A
edge is 5 nodes A C leaders A C
state of dict is dict_keys(['A'])
adding C to group A
updated nodes A C leaders A A
edge is 6 nodes C E leaders A A
state of dict is dict_keys(['A'])
both nodes part of A do nothing
updated nodes C E leaders A A
edge is 7 nodes B C leaders A A
state of dict is dict_keys(['A'])
both nodes part of A do nothing
updated nodes B C leaders A A
A
B
D
E
C
edges [1, 2, 3, 5]
因此,代码应以图形中的所有节点具有单个父级结尾。至少这是我对Kruskal算法的理解。它不在较大的图中,但是我不能在此处发布此示例。基于此代码的任何想法都将不胜感激。
答案 0 :(得分:0)
“因此,代码应以图形中的所有节点具有单个父级结尾。”
不!代码应以图中的所有节点都属于单个连接的组件结尾,但这并不意味着它们在联合查找数据结构中都具有相同的父代。数据结构定义两个节点具有相同的根节点,但它们可能没有相同的父级。
要更正UnionFind
类的实现,我们需要使returnLeader
方法搜索根节点,而不仅仅是返回父节点:
def returnLeader(self):
cur = self
while cur != cur.leader:
cur = cur.leader
return cur
这在逻辑上是正确的,但是我们可以通过“路径压缩”来提高大型输入的效率。为了避免多次执行相同的搜索,只要搜索找到其他根节点,就更新领导者。如果我们递归调用returnLeader
,那么它将也更新根节点路径上的所有节点。
def returnLeader(self):
if self.leader != self.leader.leader:
self.leader = self.leader.returnLeader()
return self.leader