最小生成樹的概念:
最小生成樹是基於“帶權圖” 的,即圖中每條邊上都有特定的權值,這樣的圖又稱為網。最小生成樹指的是所有生成樹中,權值之和最小的樹。
Prim算法:
假設G=(V,E)為一網圖,其中V為頂點的集合,E為邊的集合。從某一頂點u1出發,選擇與它關聯的具有最小權值的邊(u1, v),將其頂點v加入到生成樹頂點
集合U中。U用於存放G的最小生成樹中的頂點,T存放G的最小生成樹中的邊。
令集合U的初值為U={u1} (假設構造最小生成樹時,從頂點u1出發),集合T的初值為T={ }。
以后每一步從U中選擇一個頂點u(u屬於U),而另一個頂點v屬於V-U的邊中,選取具有最小權值的邊(u,v),將頂點v加入集合U中,將邊(u,v)加入集合T
中,如此不斷重復,直到U=V時,最小生成樹構造完畢,這時集合T中包含了最小生成樹的所有邊。
Prim算法的描述:
(1)U={u1} , T={ }
(2) while(U<>V)
(u,v)=min{Wuv ; u屬於U, v屬於V-U};
T=T+{ (u, v) };
U=U + { v }.
(3) 結束
在構造過程中,設置了兩個輔助數組:lowcost[]存放生成樹頂點集合U內頂點到生成樹外V-U各頂點的各邊上的當前最小權值;
nearvex[]記錄生成樹頂點集合外各頂點距離集合內哪個頂點最近(即權值最小).
若選擇從頂點0出發,即u0 =0, 則兩個輔助數組的初始狀態為:
反復做以下工作:
(1) 在lowcost[ ]中選擇nearvec[ ]<>-1 && lowcost[i]最小的邊,用v標記它。則選中的權值最小的邊為(nearvex[v] , v),相應的權值為lowcost[v]。
程序如下:
//
// main.cpp
// Prim
//
// Created by duanqibo on 2019/7/3.
// Copyright © 2019年 duanqibo. All rights reserved.
// Prim普利姆算法建立最小生成樹
#include <iostream>
#include <stdio.h>
#define MaxVerNum 100
#define MaxValue 10000
typedef struct{
char vexs[MaxVerNum]; //頂點集合
int edges[MaxVerNum][MaxVerNum]; //邊集合
int n,e; //頂點和邊
}MGraph;
char vertex[]="0123456";
int nvertex=7,nedges=9;
int connection[][3]={{0,1,28},{0,5,10},{1,2,16},{1,6,14},{2,3,12},{3,4,22},{3,6,18},{4,5,25},{4,6,24}};
void CreateMgraph(MGraph &G)
{
int i,j,k;
G.n=nvertex;
G.e=nedges;
for(i=0;i<G.n;i++)
G.vexs[i]=vertex[i]; //頂點
for(i=0;i<G.n;i++)
for(j=0;j<G.n;j++)
G.edges[i][j]=MaxValue; //初始化邊最大值,沒有邊
for(i=0;i<G.n;i++)
G.edges[i][i]=0; //初始化邊為0
for(k=0;k<G.e;k++)
{
i=connection[k][0];
j=connection[k][1];
G.edges[i][j]=connection[k][2];
G.edges[j][i]=G.edges[i][j]; //有向圖沒有這一行
}
}
void printMgraph(MGraph &G)
{
int i,j;
printf("圖的結點總數:%d 邊總數:%d\n",G.n,G.e);
for(i=0;i<G.n;i++)
{
for(j=0;j<G.n;j++)
if(G.edges[i][j]==10000)
printf("∞ "); //"00"代表無窮
else
printf("%d ",G.edges[i][j]);
printf("\n");
}
}
//最小生成樹
typedef struct
{
int head,tail,cost;
}MST[MaxVerNum];
void Prim(MGraph &G,MST &T,int u)
{
int i,j;
int *lowcost=new int[G.n];
int *nearvex=new int[G.n];
for(i=0;i<G.n;i++)
{
lowcost[i]=G.edges[u][i]; //u到各點的代價
nearvex[i]=u; //最短帶權路徑
}
nearvex[u]=-1; //加入到生成樹頂點集合
int k=0;
for(i=0;i<G.n;i++)
if(i!=u)
{
int min=MaxValue;
int v=u;
for(j=0;j<G.n;j++)
if(nearvex[j]!=-1 && lowcost[j]<min) //=-1不參選
{
v=j;
min=lowcost[j];//求生成樹外頂點到生成樹內頂點具有最小權值的邊,
//v是當前具有最小權值的邊
}
if(v!=u)
{
T[k].tail=nearvex[v];
T[k].head=v;
T[k++].cost=lowcost[v];
nearvex[v]=-1; //該邊加入生成樹標記
for(j=0;j<G.n;j++)
if(nearvex[j]!=-1 && G.edges[v][j]<lowcost[j])
{
lowcost[j]=G.edges[v][j]; //修改
nearvex[j]=v;
}
}
}//循環n-1次,加入n-1條邊
}
int main(int argc, const char * argv[]) {
int i;
MGraph g;
CreateMgraph(g);
printMgraph(g);
MST t;
Prim(g,t,0);
printf("生成樹:結點->權值->結點\n");
for(i=0;i<g.n;i++)
printf("(%d)-->%d-->(%d)\n",t[i].tail,t[i].cost,t[i].head);
return 1;
}
運行結果如下: