最小生成树 (Prim和Kruskal)

1、问题

  在一给定的无向图G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边,而 w(u, v) 代表此边的权重,求存在 T 为 E 的子集且为无循环图,使得的 w(T) 最小。

2、解析

  Prim算法实现步骤:

   1、随机取一个点设为起点,加入到集合S中

   2、找到一个不属于集合S的点P,且该点距离集合S最近

   3、将点P加入到集合S中,并松弛所有和点P相连且未加入集合S的点

   4、重复步骤2、3直接所有点都加入到集合S

   

 

  Kruskal算法实现步骤:

   1、将所有边加入到集合S中

   2、选择一条未选择过的最短边(u,v),若点u和点v不属于同一个集合,将点u并入点v的集合(该操作用并查集实现)

   3、重复步骤2,直到将n-1条边加入到最小生成树

  

3、设计

      Prim算法伪代码:

 1 int Prim(int graph[][N], bool vis[], int dis[],int n) {
 2     //graph[]里面存储的是点之间的距离  
 3     //dis[]存储的是初始点到各点的距离
 4     //vis[]存储该点是否被标记过
 5     //n代表点的个数
 6 
 7     ans <- 0;//最小生成树的权值和
 8 
 9     for i <- 1 to n    //初始化操作
10         vis[i] = false;
11         dis[i] = graph[1][i];
12 
13     vis[1] <- true//标记初始点
14     for i <- 2 to n
15         minn  <- 0x3f3f3f3f
16         index <- -1
17         for j <- 1 to n
18             if vis[j] <- false and dis[j] < minn
19                 //寻找未标记点集合中距离已标记点集合最近的点,若找到,记录下标,修改minn
20                 minn <- dis[j]
21                 index <- j
22         if index == -1//未找到可以进行标记的点,说明该图不连通
23             return -1
24         
25         vis[index] <- true//找到的点打上标记
26         ans <- ans+minn//加上该段距离的权值
27         for (j <- 1 to n
28             //通过找到的点对所有未标记的点进行松弛操作
29             if vis[j] == false and graph[index][j] < dis[j]
30                 dis[j] <- graph[index][j]
31     return ans//返回权值和
32 }

 

   Kruskal算法伪代码:

 1 int find(int x){
 2     if x==fa[x] 
 3         return x
 4     else 
 5         return fa[x] <- find(fa[x])
 6     //路径压缩+查找祖先节点 
 7 }
 8 
 9 int kruskal(){
10     ans <- 0
11     cnt <- 0
12     sort(edge, edge + m, cmp)//给边排序 
13     for i = 0 to m
14         fu = find(edge[i].u)//查找顶点u的祖先节点 
15         fv = find(edge[i].v)//查找顶点v的祖先节点 
16         if fu  != fv //如果不构成环,在MST中加上该条边 
17             ans <- ans + edge[i].w
18             fa[fv] = fu//跟新顶点v的父节点 
19             cnt <- cnt + 1 
20             if cnt == n - 1
21                 return ans// 找到MST返回MST的权值 
22     return -1
23 }

4、分析   

  Prim算法时间复杂度分析:

   进行n次加入集合操作,时间复杂度O(n)

   在n次操作中,查找最近的点操作,时间复杂度O(n);松弛所有未标记的点,时间复杂度O(n)

   总时间复杂度O(n^2)

 

 Kruskal算法时间复杂度分析:

     m条边使用sort升序排序时间复杂度O(m*log(m))

   往空图中加入m条边时间复杂度O(m),每加入一条边用并查集判断是否构成环,路径压缩并查集查找祖先节点时间复杂度O(log(m))

   总时间复杂度O(m*log(m))

 

5、源码

https://github.com/ChenyuWu0705/Algorithm-Analyze-and-Design/blob/main/Kruskal.cpp

https://github.com/ChenyuWu0705/Algorithm-Analyze-and-Design/blob/main/Prim.cpp

posted @ 2021-03-14 19:49  programmer_w  阅读(176)  评论(0编辑  收藏  举报