1. 程式人生 > >克魯斯卡爾演算法與普里姆演算法詳解

克魯斯卡爾演算法與普里姆演算法詳解

最近資料結構老師講了好幾個演算法,今晚上正好有空,所以就來整理一下

一: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);
}

五:如果大家覺得這篇部落格有問題,歡迎提意見,博主會認真學習噠~