贪心算法实例(五)最小生成树(Kruskal算法)
定义
无回路的无向图称为树图。
最小生成树(Minimum Spanning Tree,MST),一个有n个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有n个结点,并且有保持图连通的最少的边。 最小生成树可以用Kruskal(克鲁斯卡尔)算法或Prim(普里姆)算法求出。
思路
Kruskal(克鲁斯卡尔)算法
克鲁斯卡尔算法,从边的角度求网的最小生成树,时间复杂度为O(eloge) —— 和普里姆算法恰恰相反,更适合于求边稀疏的网的最小生成树。
具体思路是:将所有边按照权值的大小进行升序排序,然后从小到大一一判断,条件为:如果这个边不会与之前选择的所有边组成回路,就可以作为最小生成树的一部分;反之,舍去。直到具有 n 个顶点的连通网筛选出来 n-1 条边为止。筛选出来的边和所有的顶点构成此连通网的最小生成树。
实现
Kruskal(克鲁斯卡尔)算法
# Python 3: Kruskal for MST class Vertex(object): def __init__(self, name: str, value: int = -1): self.name: str = name self.value: int = value self.dist = float('Inf') # shortest distance from source vertex. self.prev = None # previous vertex with shortest distance class Edge(object): def __init__(self, start: Vertex, end: Vertex, weight: int): self.start: Vertex = start self.end: Vertex = end self.weight: int = weight class Graph: def __init__(self, V, E): """ :param V: a collection of vertex :param E: a collection of edges """ self.V = V self.E = E
# 以全局变量X定义节点集合,即类似{'A':'A','B':'B','C':'C','D':'D'},如果A、B两点连通,则会更改为{'A':'B','B':'B",...},即任何两点连通之后,两点的值value将相同。 X = dict() # 各点的初始等级均为0,如果被做为连接的的末端,则增加1 R = dict() def make_set(v): X[v] = v R[v] = 0 def find_set(v): if X[v] != v: X[v] = find_set(X[v]) return X[v] def union(v1, v2): r1 = find_set(v1) r2 = find_set(v2) if r1 != r2: if R[r1] > R[r2]: X[r2] = r1 else: X[r1] = r2 if R[r1] == R[r2]: R[r2] += 1 def kruskal(G: Graph): A = [] # record MST X = dict() R = dict() for v in G.V: make_set(v) G.E.sort(key=lambda e: e.weight, reverse=False) # sorted from small to large for e in G.E: if find_set(e.start) != find_set(e.end): A.append(e) union(e.start, e.end) return A """ a = Vertex('a', -1) b = Vertex('b', -1) h = Vertex('h', -1) i = Vertex('i', -1) c = Vertex('c', -1) g = Vertex('g', -1) d = Vertex('d', -1) f = Vertex('f', -1) e = Vertex('e', -1) V = [a, b, h, i, c, g, d, f, e] E = [ Edge(a, b, 4), Edge(b, a, 4), Edge(a, h, 8), Edge(h, a, 8), Edge(b, h, 11), Edge(h, b, 11), Edge(h, i, 7), Edge(i, h, 7), Edge(g, h, 1), Edge(h, g, 1), Edge(c, i, 2), Edge(i, c, 2), Edge(b, c, 8), Edge(c, b, 8), Edge(i, g, 6), Edge(g, i, 6), Edge(c, f, 4), Edge(f, c, 4), Edge(c, d, 7), Edge(d, c, 7), Edge(d, f, 14), Edge(f, d, 14), Edge(g, f, 2), Edge(f, g, 2), Edge(d, e, 9), Edge(e, d, 9), Edge(e, f, 10), Edge(f, e, 10), ] G = Graph(V, E) S = kruskal(G) for e in S: print(f'{e.start.name} -> {e.end.name}: {e.weight}') ---- g -> h: 1 c -> i: 2 g -> f: 2 a -> b: 4 c -> f: 4 c -> d: 7 a -> h: 8 d -> e: 9 """
参考
Kruskal(克鲁斯卡尔)算法:Kruskal算法(Python实现)_johnjim0816的博客-CSDN博客_kruskal算法 python