贪心算法实例(六)最小生成树(Prim算法)
定义
无回路的无向图称为树图。
最小生成树(Minimum Spanning Tree,MST),一个有n个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有n个结点,并且有保持图连通的最少的边。 最小生成树可以用Kruskal(克鲁斯卡尔)算法或Prim(普里姆)算法求出。
思路
Prim(普里姆)算法
普里姆算法查找最小生成树的过程,采用了贪心算法的思想。对于包含 N 个顶点的连通网,普里姆算法每次从连通网中找出一个权值最小的边作为最小生成树的结点(一开始需要输入一个顶点作为根结点),这样的操作重复 N-1 次,由 N-1 条权值最小的边组成的生成树就是最小生成树。
实现
Prim(普里姆)算法
# Python 3: Prim 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 def prim(G, r: Vertex): S = [] for u in G.V: u.dist = float('Inf') u.prev = None r.dist = 0 Q = G.V while Q: u = extract_min(Q) for e in G.E: if e.start == u and e.end in Q and e.weight < e.end.dist: e.end.prev = u e.end.dist = e.weight S.append(u) Q.remove(u) return S def extract_min(Q): """ extract the vertex with the shortest path to current vertex. :param Q: a collection of vertex :return: """ tv = None tdist = float('Inf') for v in Q: if v.dist < tdist: tdist = v.dist tv = v return tv """ 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, 9), Edge(h, a, 9), 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 = a S = prim(G, s) print(f'{s.name}: {s.dist}') for i in range(1, len(S)): print(f'{S[i].prev.name} -> {S[i].name}: {S[i].dist}') ---- a: 0 a -> b: 4 b -> c: 8 c -> i: 2 c -> f: 4 f -> g: 2 g -> h: 1 c -> d: 7 d -> e: 9 """