对一个连通图G=(V,E),每条边到实数R的映射为w,我们可以构造生成树T=(V,E'),其中E'是E的子集,w(T)=w(e1)+w(e2)+...+w(en),ei属于E'。而图的最小生成树问题则是寻找这一系列生成树中,w(T)最小的那棵,即为图的最小生成树MST。
所有关于最小生成树的算法都涉及贪心算法,即局部最优会得到全局最优。
算法的中心思想是,去掉MST中的某条边e,MST分成两个部分T1,T2,则T1,T2是由其各自顶点所引导的子图的MST,并且对所有连接T1,T2的边ei,w(e)<=w(ei)。
(1)Prim算法
从一个顶点开始,依次加入距离当前生成树的最近的点,以及其对应的边。
dist[i]表示第i个点到当前T的最短边的长度,e(i)表示第i个点连接当前T的最短边。
Procedure PRIM(G,w;T)
dist[1]=0,S=empty,T=empty
for i=2 to n:
dist[i]=INF
prev[i]=NONE
while S!=V:
choose i in V-S such that dist[i] is minimal:
S=S+{i}
if i!=1:
T=T+{e(i)}
for j in A[i] and in V-S:
if dist[j]>w(ij):
dist[j]=w(ij)
e[j]=e(i,j)
如果在选择最小的dist[i]时用线性查找,则该算法的复杂度为O(|V|*|V|),而采用以dist为key的priority_queue可以降低算法复杂度。
Procedure PRIM(G,w;T)
dist[1]=0,priority_queue Q=empty,T=empty
for i=2 to n:
dist[i]=INF
Q.push(1)
while(Q is not empty):
i=Q.front()
Q.pop()
S=S+{i}
if i!=1:
T=T+{e(i)}
for j in A[i] and in V-S:
if dist[j]>w(ij):
dist[j]=w(ij)
e[j]=e(i,j)
算法的复杂度为O(|V|log|V|)
(2)Kruskal算法
依次加入当前连接不同块的最短边。确定不同块需要数据结构disjoint set。
Procedure KRUSKAL(G,w;T)
T=empty
for i=1 to n:
Vi={i}
put E into a priority_queue Q with priority function w
while Q is not emmpty:
e=Q.front()
Q.pop()
u,v is the vertices of e
find the components Vu and Vv containing u and v
if Vu!=Vv:
merge(Vu,Vv)
T=T+{e}
算法的复杂度为O(|E|log|E|)