克魯斯卡爾演算法與普里姆演算法詳解
阿新 • • 發佈:2018-12-20
最近資料結構老師講了好幾個演算法,今晚上正好有空,所以就來整理一下
一:Kruskal演算法思想:直接以邊為目標去構建最小生成樹,注意只找出n-1條邊即可,並且不能形成迴路。圖的儲存結構採用的是邊集陣列,且權值相等的邊在陣列中的排練次序是任意的,如果圖中的邊數較多則此演算法會很浪費時間!
二:Prim演算法思想:以某一頂點為起點,一點一點的去找各頂點上最小權值的邊來構建最小生成樹。圖的儲存結構是鄰接矩陣,此方法需要一個頂點集合T,開始的時候為空集,慢慢的會將連通的頂點陸續的加入到集合中,全部頂點都加入集合以後,就得到我們所需要的最小生成樹啦!
三:克魯斯卡爾演算法程式碼實現
#include "stdio.h" #include "stdlib.h" struct edge { int m; int n; int d; }a[5010]; //克魯斯卡爾演算法採用邊集陣列來儲存圖 int cmp(const void *a,const void *b) //按升序排列,此過程也可以用快排來實現! { return ((struct edge *)a)->d>((struct edge *)b)->d; } int main(void) { int i,n,t,num,min,k,g,x[100]; printf("請輸入頂點的個數:"); scanf("%d",&n); t=n*(n-1)/2; //n個頂點的無向連通圖共有n*(n-1)/2條邊 for(i=1;i<=n;i++) x[i]=i; printf("請輸入每條邊的起始端點、權值:/n"); for(i=0;i<t;i++) scanf("%d %d %d",&a[i].m,&a[i].n,&a[i].d); //輸入每條邊的權值 qsort(a,t,sizeof(a[0]),cmp);//此過程可以用快排,來將每條邊權值從小到大排列起來 min=num=0; for(i=0;i<t && num<n-1;i++) { for(k=a[i].m;x[k]!=k;k=x[k]) //判斷線段的起始點所在的集合 x[k]=x[x[k]]; for(g=a[i].n;x[g]!=g;g=x[g]) //判斷線段的終點所在的集合 x[g]=x[x[g]]; if(k!=g) //如果線段的兩個端點所在的集合不一樣 { x[g]=k; min+=a[i].d; num++; printf("最小生成樹中加入邊:%d %d/n",a[i].m,a[i].n); } } printf("最小生成樹的權值為:%d/n",min); system("pause"); return 0; }
克魯斯卡爾演算法程式碼實現方法二:
typedef struct { char vertex[VertexNum]; //頂點表 int edges[VertexNum][VertexNum]; //鄰接矩陣,可看做邊表 int n,e; //圖中當前的頂點數和邊數 }MGraph; typedef struct node { int u; //邊的起始頂點 int v; //邊的終止頂點 int w; //邊的權值 }Edge; void kruskal(MGraph G) { int i,j,u1,v1,sn1,sn2,k; int vset[VertexNum]; //輔助陣列,判定兩個頂點是否連通 int E[EdgeNum]; //存放所有的邊 k=0; //E陣列的下標從0開始 for (i=0;i<G.n;i++) { for (j=0;j<G.n;j++) { if (G.edges[i][j]!=0 && G.edges[i][j]!=INF) { E[k].u=i; E[k].v=j; E[k].w=G.edges[i][j]; k++; } } } heapsort(E,k,sizeof(E[0])); //堆排序,按權值從小到大排列 for (i=0;i<G.n;i++) //初始化輔助陣列 { vset[i]=i; } k=1; //生成的邊數,最後要剛好為總邊數 j=0; //E中的下標 while (k<G.n) { sn1=vset[E[j].u]; sn2=vset[E[j].v]; //得到兩頂點屬於的集合編號 if (sn1!=sn2) //不在同一集合編號內的話,把邊加入最小生成樹 { printf("%d ---> %d, %d",E[j].u,E[j].v,E[j].w); k++; for (i=0;i<G.n;i++) { if (vset[i]==sn2) { vset[i]=sn1; } } } j++; } }
四:Prim演算法具體實現
#define MAX 100000
#define VNUM 10+1 //這裡沒有ID為0的點,so id號範圍1~10
int edge[VNUM][VNUM]={/*輸入的鄰接矩陣*/};
int lowcost[VNUM]={0}; //記錄Vnew中每個點到V中鄰接點的最短邊
int addvnew[VNUM]; //標記某點是否加入Vnew
int adjecent[VNUM]={0}; //記錄V中與Vnew最鄰近的點
void prim(int start)
{
int sumweight=0;
int i,j,k=0;
for(i=1;i<VNUM;i++) //頂點是從1開始
{
lowcost[i]=edge[start][i];
addvnew[i]=-1; //將所有點至於Vnew之外,V之內,這裡只要對應的為-1,就表示在Vnew之外
}
addvnew[start]=0; //將起始點start加入Vnew
adjecent[start]=start;
for(i=1;i<VNUM-1;i++)
{
int min=MAX;
int v=-1;
for(j=1;j<VNUM;j++)
{
if(addvnew[j]!=-1&&lowcost[j]<min) //在Vnew之外尋找最短路徑
{
min=lowcost[j];
v=j;
}
}
if(v!=-1)
{
printf("%d %d %d\n",adjecent[v],v,lowcost[v]);
addvnew[v]=0; //將v加Vnew中
sumweight+=lowcost[v]; //計算路徑長度之和
for(j=1;j<VNUM;j++)
{
if(addvnew[j]==-1&&edge[v][j]<lowcost[j])
{
lowcost[j]=edge[v][j]; //此時v點加入Vnew 需要更新lowcost
adjecent[j]=v;
}
}
}
}
printf("the minmum weight is %d",sumweight);
}
五:如果大家覺得這篇部落格有問題,歡迎提意見,博主會認真學習噠~