最小生成树:构成连通网的最小代价生成树。即用n-1条边将一个n个顶点的连通图连接起来,并使得权值的和最小。
最小权值和而非最短路径,即不为一笔画。
普里姆(Prim)算法
定义:假设N = {P,{E}}是连通网,TE是N上最小生成树中边的集合。算法从U={u0}(u0∈V),TE={}开始。重复执行下述操作:在所有u∈U,v∈V-U的边(u,v)∈E中找一条代价最小的边(u0,v0)并入TE,同时v0并入U,直到U=V为止。此时TE中必有n-1条边,则T=(V,{TE})为N的最小生成树。
一句话概括:从一个顶点v0出发,找当前已纳入最小生成树的整体向外扩散的最小权值,直到所有顶点均被纳入最小生成树为止。
代码:
1 | void MiniSpanTree_Prim (MGraph G) |
由循环嵌套可知时间复杂度为O(n²)。
克鲁斯卡尔(Kruskal)算法
定义:假设N = {P,{E}}是连通网,则令最小生成树的初始状态为只有n个顶点而无边的非连通图T={V,{}},图中每个顶点自成一个连通分量。在E中选择代价最小的边,若该边依附的顶点落在T中不同连通分量上,则将此边加入到T中,否则舍去此边而选择下一条代价最小的边。依次类推,直到所有顶点都在同一连通分量上为止。
这里采用边集数组:
1 | typedef struct |
代码:
1 | void MiniSpanTree_Kruskal (MGraph G) |
理解:例如当i = 6时parent数组为:{1,5,8,7,7,8,0,0,6},从parent[0] = 1开始可构建两条指针链:
可以认为每个连通分量只提供一个指向parent数组中值为0的项的API接口,若某条边能够形成环路,则其begin和end端经过Find函数后得到的n和m必定相等,此即为判断一条边是否满足要求的判断依据。
此算法的Find函数由边数e决定,时间复杂度为O(loge),而外面有一个for循环e次,故Kruskal算法的时间复杂度为O(eloge)。(先记下,以后再想原因)
Kruskal算法主要针对边来展开,边数少时效率非常高,对于稀疏图有很大优势;Prim算法对于稠密图,即边数非常多的情况会更有优势。