Skip to content

Commit 971b6de

Browse files
committed
无向图最小生成树Kruskal算法
1 parent 1c77e48 commit 971b6de

File tree

3 files changed

+454
-0
lines changed

3 files changed

+454
-0
lines changed

kruskal_algorithm.md

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
#问题
2+
3+
最小生成树的Kruskal算法
4+
5+
描述:有A、B、C、D四个点,每两个点之间的距离(无方向)是(第一个数字是两点之间距离,后面两个字母代表两个点):(1,'A','B'),(5,'A','C'),(3,'A','D'),(4,'B','C'),(2,'B','D'),(1,'C','D')
6+
生成边长和最小的树,也就是找出一种连接方法,将各点连接起来,并且各点之间的距离和最小。
7+
8+
#思路说明:
9+
10+
Kruskal算法是经典的无向图最小生成树解决方法。此处列举两种python的实现方法。这两种方法均参考网上,并根据所学感受进行了适当改动。
11+
12+
#解决1(Python)
13+
14+
#! /usr/bin/env python
15+
#coding:utf-8
16+
17+
#以全局变量X定义节点集合,即类似{'A':'A','B':'B','C':'C','D':'D'},如果A、B两点联通,则会更改为{'A':'B','B':'B",...},即任何两点联通之后,两点的值value将相同。
18+
19+
X = dict()
20+
21+
#各点的初始等级均为0,如果被做为连接的的末端,则增加1
22+
23+
R = dict()
24+
25+
#设置X R的初始值
26+
27+
def make_set(point):
28+
X[point] = point
29+
R[point] = 0
30+
31+
#节点的联通分量
32+
33+
def find(point):
34+
if X[point] != point:
35+
X[point] = find(X[point])
36+
return X[point]
37+
38+
#连接两个分量(节点)
39+
40+
def merge(point1,point2):
41+
r1 = find(point1)
42+
r2 = find(point2)
43+
if r1 != r2:
44+
if R[r1] > R[r2]:
45+
X[r2] = r1
46+
else:
47+
X[r1] = r2
48+
if R[r1] == R[r2]: R[r2] += 1
49+
50+
#KRUSKAL算法实现
51+
52+
def kruskal(graph):
53+
for vertice in graph['vertices']:
54+
make_set(vertice)
55+
56+
minu_tree = set()
57+
58+
edges = list(graph['edges'])
59+
edges.sort() #按照边长从小到达排序
60+
for edge in edges:
61+
weight, vertice1, vertice2 = edge
62+
if find(vertice1) != find(vertice2):
63+
merge(vertice1, vertice2)
64+
minu_tree.add(edge)
65+
return minu_tree
66+
67+
68+
if __name__=="__main__":
69+
70+
graph = {
71+
'vertices': ['A', 'B', 'C', 'D', 'E', 'F'],
72+
'edges': set([
73+
(1, 'A', 'B'),
74+
(5, 'A', 'C'),
75+
(3, 'A', 'D'),
76+
(4, 'B', 'C'),
77+
(2, 'B', 'D'),
78+
(1, 'C', 'D'),
79+
])
80+
}
81+
82+
result = kruskal(graph)
83+
print result
84+
85+
"""
86+
参考:
87+
1.https://github.com/qiwsir/Algorithms-Book--Python/blob/master/5-Greedy-algorithms/kruskal.py
88+
2.《算法基础》(GILLES Brassard,Paul Bratley)
89+
"""
90+
91+
92+
#解决2(Python)
93+
94+
以下代码参考http://www.ics.uci.edu/~eppstein/PADS/的源码
95+
96+
#! /usr/bin/env python
97+
#coding:utf-8
98+
99+
import unittest
100+
101+
class UnionFind:
102+
"""
103+
UnionFind的实例:
104+
Each unionFind instance X maintains a family of disjoint sets of
105+
hashable objects, supporting the following two methods:
106+
107+
- X[item] returns a name for the set containing the given item.
108+
Each set is named by an arbitrarily-chosen one of its members; as
109+
long as the set remains unchanged it will keep the same name. If
110+
the item is not yet part of a set in X, a new singleton set is
111+
created for it.
112+
113+
- X.union(item1, item2, ...) merges the sets containing each item
114+
into a single larger set. If any item is not yet part of a set
115+
in X, it is added to X as one of the members of the merged set.
116+
"""
117+
118+
def __init__(self):
119+
"""Create a new empty union-find structure."""
120+
self.weights = {}
121+
self.parents = {}
122+
123+
def __getitem__(self, object):
124+
"""Find and return the name of the set containing the object."""
125+
126+
# check for previously unknown object
127+
if object not in self.parents:
128+
self.parents[object] = object
129+
self.weights[object] = 1
130+
return object
131+
132+
# find path of objects leading to the root
133+
path = [object]
134+
root = self.parents[object]
135+
while root != path[-1]:
136+
path.append(root)
137+
root = self.parents[root]
138+
139+
# compress the path and return
140+
for ancestor in path:
141+
self.parents[ancestor] = root
142+
return root
143+
144+
def __iter__(self):
145+
"""Iterate through all items ever found or unioned by this structure."""
146+
return iter(self.parents)
147+
148+
def union(self, *objects):
149+
"""Find the sets containing the objects and merge them all."""
150+
roots = [self[x] for x in objects]
151+
heaviest = max([(self.weights[r],r) for r in roots])[1]
152+
for r in roots:
153+
if r != heaviest:
154+
self.weights[heaviest] += self.weights[r]
155+
self.parents[r] = heaviest
156+
157+
158+
"""
159+
Various simple functions for graph input.
160+
161+
Each function's input graph G should be represented in such a way that "for v in G" loops through the vertices, and "G[v]" produces a list of the neighbors of v; for instance, G may be a dictionary mapping each vertex to its neighbor set.
162+
163+
D. Eppstein, April 2004.
164+
"""
165+
166+
def isUndirected(G):
167+
"""Check that G represents a simple undirected graph."""
168+
for v in G:
169+
if v in G[v]:
170+
return False
171+
for w in G[v]:
172+
if v not in G[w]:
173+
return False
174+
return True
175+
176+
177+
def union(*graphs):
178+
"""Return a graph having all edges from the argument graphs."""
179+
out = {}
180+
for G in graphs:
181+
for v in G:
182+
out.setdefault(v,set()).update(list(G[v]))
183+
return out
184+
185+
186+
"""
187+
Kruskal's algorithm for minimum spanning trees. D. Eppstein, April 2006.
188+
"""
189+
190+
def MinimumSpanningTree(G):
191+
"""
192+
Return the minimum spanning tree of an undirected graph G.
193+
G should be represented in such a way that iter(G) lists its
194+
vertices, iter(G[u]) lists the neighbors of u, G[u][v] gives the
195+
length of edge u,v, and G[u][v] should always equal G[v][u].
196+
The tree is returned as a list of edges.
197+
"""
198+
if not isUndirected(G):
199+
raise ValueError("MinimumSpanningTree: input is not undirected")
200+
for u in G:
201+
for v in G[u]:
202+
if G[u][v] != G[v][u]:
203+
raise ValueError("MinimumSpanningTree: asymmetric weights")
204+
205+
# Kruskal's algorithm: sort edges by weight, and add them one at a time.
206+
# We use Kruskal's algorithm, first because it is very simple to
207+
# implement once UnionFind exists, and second, because the only slow
208+
# part (the sort) is sped up by being built in to Python.
209+
subtrees = UnionFind()
210+
tree = []
211+
for W,u,v in sorted((G[u][v],u,v) for u in G for v in G[u]):
212+
if subtrees[u] != subtrees[v]:
213+
tree.append((u,v))
214+
subtrees.union(u,v)
215+
return tree
216+
217+
218+
# If run standalone, perform unit tests
219+
220+
class MSTTest(unittest.TestCase):
221+
def testMST(self):
222+
"""Check that MinimumSpanningTree returns the correct answer."""
223+
G = {0:{1:11,2:13,3:12},1:{0:11,3:14},2:{0:13,3:10},3:{0:12,1:14,2:10}}
224+
T = [(2,3),(0,1),(0,3)]
225+
for e,f in zip(MinimumSpanningTree(G),T):
226+
self.assertEqual(min(e),min(f))
227+
self.assertEqual(max(e),max(f))
228+
229+
if __name__ == "__main__":
230+
unittest.main()

kruskal_algorithm.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#! /usr/bin/env python
2+
#coding:utf-8
3+
4+
"""
5+
最小生成树的Kruskal算法
6+
7+
描述:有A、B、C、D四个点,每两个点之间的距离(无方向)是(第一个数字是两点之间距离,后面两个字母代表两个点):(1,'A','B'),(5,'A','C'),(3,'A','D'),(4,'B','C'),(2,'B','D'),(1,'C','D')
8+
生成边长和最小的树,也就是找出一种连接方法,将各点连接起来,并且各点之间的距离和最小。
9+
10+
"""
11+
12+
#以全局变量X定义节点集合,即类似{'A':'A','B':'B','C':'C','D':'D'},如果A、B两点联通,则会更改为{'A':'B','B':'B",...},即任何两点联通之后,两点的值value将相同。
13+
14+
X = dict()
15+
16+
#各点的初始等级均为0,如果被做为连接的的末端,则增加1
17+
18+
R = dict()
19+
20+
#设置X R的初始值
21+
22+
def make_set(point):
23+
X[point] = point
24+
R[point] = 0
25+
26+
#节点的联通分量
27+
28+
def find(point):
29+
if X[point] != point:
30+
X[point] = find(X[point])
31+
return X[point]
32+
33+
#连接两个分量(节点)
34+
35+
def merge(point1,point2):
36+
r1 = find(point1)
37+
r2 = find(point2)
38+
if r1 != r2:
39+
if R[r1] > R[r2]:
40+
X[r2] = r1
41+
else:
42+
X[r1] = r2
43+
if R[r1] == R[r2]: R[r2] += 1
44+
45+
#KRUSKAL算法实现
46+
47+
def kruskal(graph):
48+
for vertice in graph['vertices']:
49+
make_set(vertice)
50+
51+
minu_tree = set()
52+
53+
edges = list(graph['edges'])
54+
edges.sort() #按照边长从小到达排序
55+
for edge in edges:
56+
weight, vertice1, vertice2 = edge
57+
if find(vertice1) != find(vertice2):
58+
merge(vertice1, vertice2)
59+
minu_tree.add(edge)
60+
return minu_tree
61+
62+
63+
if __name__=="__main__":
64+
65+
graph = {
66+
'vertices': ['A', 'B', 'C', 'D', 'E', 'F'],
67+
'edges': set([
68+
(1, 'A', 'B'),
69+
(5, 'A', 'C'),
70+
(3, 'A', 'D'),
71+
(4, 'B', 'C'),
72+
(2, 'B', 'D'),
73+
(1, 'C', 'D'),
74+
])
75+
}
76+
77+
result = kruskal(graph)
78+
print result
79+
80+
"""
81+
参考:
82+
1.https://github.com/qiwsir/Algorithms-Book--Python/blob/master/5-Greedy-algorithms/kruskal.py
83+
2.《算法基础》(GILLES Brassard,Paul Bratley)
84+
"""

0 commit comments

Comments
 (0)