|
| 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() |
0 commit comments