文章目录
- @[toc]
- 问题描述
- 最小生成树的性质
- 证明
- `Prim`算法
- `Prim`算法的正确性
- 时间复杂性
- `Python`实现
文章目录
- @[toc]
- 问题描述
- 最小生成树的性质
- 证明
- `Prim`算法
- `Prim`算法的正确性
- 时间复杂性
- `Python`实现
个人主页:丷从心
系列专栏:贪心算法
问题描述
- 设 G = ( V , E ) G = (V , E) G=(V,E)是无向连通带权图, E E E中每条边 ( v , w ) (v , w) (v,w)的权为 c [ v ] [ w ] c[v][w] c[v][w]
- 如果 G G G的一个子图 G ′ G^{'} G′是一棵包含 G G G的所有顶点的树,则称 G ′ G^{'} G′为 G G G的生成树
- 生成树上各边权的总和称为该生成树的耗费,在 G G G的所有生成树中,耗费最小的生成树称为 G G G的最小生成树
最小生成树的性质
- 设 G = ( V , E ) G = (V , E) G=(V,E)是连通带权图, U U U是 V V V的真子集,如果 ( u , v ) ∈ E (u , v) \in E (u,v)∈E,且 u ∈ U u \in U u∈U, v ∈ V − U v \in V - U v∈V−U,且在所有这样的边中, ( u , v ) (u , v) (u,v)的权 c [ u ] [ v ] c[u][v] c[u][v]最小,那么一定存在 G G G的一棵最小生成树,它以 ( u , v ) (u , v) (u,v)为其中一条边
- 这个性质有时也称为 M S T MST MST性质
证明
- 假设 G G G的任何一棵最小生成树都不包含边 ( u , v ) (u , v) (u,v),将边 ( u , v ) (u , v) (u,v)添加到 G G G的一棵最小生成树 T T T上,将产生含有边 ( u , v ) (u , v) (u,v)的圈,并且在这个圈上有一条不同于 ( u , v ) (u , v) (u,v)的边 ( u ′ , v ′ ) (u^{'} , v^{'}) (u′,v′),使得 u ′ ∈ U u^{'} \in U u′∈U, v ′ ∈ V − U v^{'} \in V - U v′∈V−U,如下图所示
- 将边 ( u ′ , v ′ ) (u^{'} , v^{'}) (u′,v′)删去,得到 G G G的另一棵生成树 T ′ T^{'} T′,由于 c [ u ] [ v ] ≤ c [ u ′ ] [ v ′ ] c[u][v] \leq c[u^{'}][v^{'}] c[u][v]≤c[u′][v′],所以 T ′ T^{'} T′的耗费 ≤ T \leq T ≤T的耗费,于是 T ′ T^{'} T′是一棵含有边 ( u , v ) (u , v) (u,v)的最小生成树,与假设矛盾
Prim
算法
- 设 G = ( V , E ) G = (V , E) G=(V,E)是连通带权图, V = { 1 , 2 , ⋯ , n } V = \set{1 , 2 , \cdots , n} V={1,2,⋯,n}
- 首先置 S = { 1 } S = \set{1} S={1},然后,只要 S S S是 V V V的真子集,就做如下贪心选择
- 选取满足条件 i ∈ S i \in S i∈S, j ∈ V − S j \in V - S j∈V−S,且 c [ i ] [ j ] c[i][j] c[i][j]最小的边,将边 ( i , j ) (i , j) (i,j)添加到边集 T T T中,并将顶点 j j j添加到 S S S中
- 这个过程一直进行到 S = V S = V S=V时为止,选取到的所有边恰好构成 G G G的一棵最小生成树
Prim
算法的正确性
- 算法结束时,边集 T T T中包含 G G G的 n − 1 n - 1 n−1条边,利用最小生成树性质和数学归纳法容易证明,边集 T T T始终包含 G G G的某棵最小生成树中的边,因此,算法结束时, T T T中的所有边构成 G G G的一棵最小生成树
时间复杂性
- 对于一个具有
n
n
n个顶点的带权无向图,
Prim
算法进行二重循环,需要 O ( n 2 ) O(n^{2}) O(n2)时间
Python
实现
import sys
class Graph:
def __init__(self, vertices):
self.V = vertices
self.graph = [[0 for _ in range(vertices)] for _ in range(vertices)]
def printMST(self, parent):
print('边\t\t权')
for i in range(1, self.V):
print(f'{parent[i]} - {i}\t{self.graph[i][parent[i]]}')
def minKey(self, key, mstSet):
min_val = sys.maxsize
min_index = None
for v in range(self.V):
if key[v] < min_val and not mstSet[v]:
min_val = key[v]
min_index = v
return min_index
def primMST(self):
key = [sys.maxsize] * self.V
parent = [None] * self.V
mstSet = [False] * self.V
key[0] = 0
parent[0] = -1
for _ in range(self.V):
u = self.minKey(key, mstSet)
mstSet[u] = True
for v in range(self.V):
if self.graph[u][v] > 0 and not mstSet[v] and self.graph[u][v] < key[v]:
key[v] = self.graph[u][v]
parent[v] = u
self.printMST(parent)
g = Graph(5)
g.graph = [
[0, 2, 0, 6, 0],
[2, 0, 3, 8, 5],
[0, 3, 0, 0, 7],
[6, 8, 0, 0, 9],
[0, 5, 7, 9, 0],
]
g.primMST()
边 权
0 - 1 2
1 - 2 3
0 - 3 6
1 - 4 5