概要:
- Prim算法
- Kruskal算法
1、Prim算法
算法流程:
(1)对图G(V,E)设置集合S来存放已被并入的顶点,然后执行n次(2)(3)
(2)每次从未并入顶点集合中选择与集合S最近的一个顶点,访问u并将其加入集合S,同时把这条离集合S最近的边加入最小生成树中。
(3)优化松弛
Prim算法与Dijkstra算法使用的思想几乎完全相同,只有在数组d[]的含义上有区别。(关于Dijkstra算法具体可以看前面写的《图的算法专题——最短路径》)
Dijkstra中d[]含义为起点s到达顶点Vi的最短距离,而Prim算法d[]含义为顶点Vi与集合S的最短距离,两者区别仅在于最短距离是到 起点 还是 集合 。
邻接矩阵版:
1 const int maxn=1000;
2 const int INF=1000000000;
3 int n,G[maxn][maxn];
4 int d[maxm]; //顶点到集合S的距离
5 bool vis[maxn]={false};
6
7 int prim(){ //默认0号为初始点,函数返回最小生成树的边权之和
8 fill(d,d+maxn,INF);
9 d[0]=0;
10 int ans=0; //边权之和
11 for(int i=0;i<n;i++){
12 int u=-1,MIN=INF;
13 for(int j=0;j<n;j++){
14 if(vis[j]==false&&d[j]<MIN){
15 u=j;
16 MIN=d[j];
17 }
18 }
19 if(u==-1) return -1;
20 vis[u]=true;
21 ans+=d[u]; //将与集合S距离最小的边加入最小生成树
22 for(int v=0;v<n;v++){
23 if(vis[v]==false && G[u][v]!=INF && G[u][v]<d[v] ){
24 d[v]=G[u][v]; //以u为中介点可以使v离集合S更近(u已被并入集合)
25 }
26 }
27 }
28 return ans;
29 }
邻接表版:
1 struct Node{
2 int v,dis; //v为目标顶点,dis为边权
3 };
4 vector<Node> Adj[maxn];
5 int n,d[maxn];
6 bool vis[maxn]={false};
7 int prim(){
8 fill(d,d+maxn,INF);
9 d[0]=0;
10 int ans=0;
11 for(int i=0;i<n;i++){
12 int u=-1,MIN=INF;
13 for(int j=0;j<n;j++){
14 if(vis[j]==false && d[j]<MIN){
15 u=j;
16 MIN=d[j];
17 }
18 }
19 if(u==-1) return -1;
20 vis[u]=true;
21 ans+=d[u];
22 for(int j=0;j<Adj[u].size();j++){
23 int v=Adj[u][j].v;
24 if(vis[v]==false && Adj[u][j].dis< d[v]){
25 d[v]=Adj[u][j].dis;
26 }
27 }
28 }
29 return ans;
30 }
2、Kruskal算法
算法流程:
(1)对所有边权从小到大排序。
(2)按边权从小到大测试所有边,如果当前测试边所连接的两个顶点不在同一个连通块中,则并入,否则舍弃。 (运用并查集)
(3)循环执行(2)直到最小生成树的边数等于总顶点数减1或是测试完所有边时结束。若结束时边数不为顶点数减1,则图不连通。
1 const int MAXV=110;
2 const int MAXE=10010;
3 int father[MAXV];
4 int findFather(int x){
5 int a=x;
6 while(x!=father[x]){
7 x=father[x];
8 }
9 while(a!=father[a]){
10 int z=a;
11 a=father[a];
12 father[z]=x;
13 }
14 return x;
15 }
16 struct edge{
17 int u,v; //边的两个端点编号
18 int cost; //边权
19 }E[MAXE];
20 bool cmp(edge a,edge b){
21 return a.cost<b.cost;
22 }
23 int kruskal(int n,int m){ //n为顶点个数,m为图的边数
24 int ans=0,Num_Edge=0;
25 for(int i=1;i<=n;i++) father[i]=i; //假设顶点范围为1到n
26 sort(E,E+m,cmp);
27 for(int i=0;i<m;i++){ //枚举边
28 int faU=findFather(E[i].u);
29 int faV=findFather(E[i].v);
30 if(faU!=faV){
31 father[faU]=faV;
32 ans+=E[i].cost;
33 Num_Edge++;
34 if(Num_Edge==n-1) break;
35 }
36 }
37 if(Num_Edge != n-1) return -1;
38 else return ans;
39 }