1. 程式人生 > >[圖論]Prim演算法求最小支撐樹和最短路徑

[圖論]Prim演算法求最小支撐樹和最短路徑

這個是以前所學,現在總結成博文一篇。

對於圖論中的求解最小支撐樹問題和最短路徑問題都有比較經典的演算法,比如最小支撐樹可以採用“破圈法”(kruskal演算法),求解最短路徑可以用“Dijkstra演算法”。這裡筆者將回顧下求解最小支撐樹的Prim演算法和最短路徑演算法。

1、Prim演算法求解最小支撐樹

例1:用Prim演算法求解圖1的一個最小支撐樹。


圖1:例圖

首先,給出Prim演算法:


實現的C語言程式碼如下:

#include<stdio.h>
int matrix[100][100];
int E[100]={0},tree[100][100]={0};
int n,i,j;
void inputmatrix()
{
	printf("請輸入鄰接矩陣的階數:\n");
	scanf("%d",&n); 
	printf("請輸入鄰接矩陣:\n");
	for(i=0;i<n;i++)
	{
		for(j=0;j<n;j++)
			scanf("%d",&matrix[i][j]);
	}	
}	


void caculate(int n,int a[100][100])
{
	int k=n-1,t=0,t1,t2,min,j,m=1,flag=1;/*k,k1作為迴圈的次數,flag標記是否有支撐樹*/
	E[0]=1;    /*先放入一個點*/
	tree[0][0]=a[0][0];
	while(k--)
	{
		min=100000;
		for(j=0;j<n;j++)
		{
			for(i=0;i<n;i++) 
				if(E[i]==1&&E[j]==0&&min>a[i][j]&&a[i][j]!=0) /*尋找符合條件的數*/
				{
					t1=j;
					t2=i;
					min=a[i][j];
				}
		}
		E[t1]=1;   /*標記為1,代表已經用過*/
		tree[t2][t1]=a[t2][t1];  /*記錄值*/
		tree[t1][t2]=a[t1][t2];  /*記錄值*/
		m++;
	}
	for(i=0;i<n;i++)
	{
		if(E[i]==0) 
		{
			printf("沒有支撐樹\n");
			flag=0;
			break;
		}
	}
	if(flag==1)  /*證明存在支撐樹*/
	{
		printf("輸出對應的最小支撐樹所對應的矩陣:\n");
		for(i=0;i<n;i++)
		{
			for(j=0;j<n;j++)
				printf("%d  ",tree[i][j]);
			printf("\n");
		} 

	}
}
main()
{
	inputmatrix();	
	caculate(n,matrix);
	system("pause");
}

針對圖1的例子,我們輸入資料求得結果:


2、改進的Prim演算法求解最短路徑

例2:利用改進的Prim演算法求解圖2的最短路徑問題。


圖2:例圖

首先給出該演算法的描述。


下面是該演算法的C語言實現。

#include<stdio.h>
int matrix[100][100];
int E[100]={0},distance[100]={0};
int path[100][100] = {0};//初試為空,記錄path 
int n,i,j,ini;//ini為初始下標 
char temp[100]; 
void inputmatrix()
{
	printf("請輸入鄰接矩陣的階數:\n");
	scanf("%d",&n); 
	printf("請輸入有向圖的鄰接矩陣(輸入0代表無窮大):\n");
	for(i=0;i<n;i++)
	{
		for(j=0;j<n;j++)
			scanf("%d",&matrix[i][j]);
	}	
	printf("請輸入需計算的起始點下標:\n");
	scanf("%d",&ini);
	E[ini-1]=1;
	path[ini-1][0]=ini; 
}	


void caculate(int n,int a[100][100])
{
	int k=n-1,t=0,t1,t2,min,j,m=1,w,flag=0;/*k,k1作為迴圈的次數,flag=1意味著可以繼續迴圈,flag=0,跳出*/
	while(k--)
	{
		min=100000;
		flag=0;// 初始化 
		for(j=0;j<n;j++)
		{
			for(i=0;i<n;i++) 
				if(E[i]==1&&E[j]==0&&min>(a[i][j]+distance[i])&&a[i][j]>0) /*尋找符合條件的數*/
				{
					flag=1;// 標記為1,找到!繼續迴圈 
					t1=j;
					t2=i;
					min=a[i][j]+distance[i]; 
				}
		}
		if(flag==0)
			break;//跳出 
		distance[t1]=min;
		E[t1]=1;   /*標記為1,代表已經用過*/
		for(j=0;j<n;j++)//把t2的值加給t1,並且最後加上t1該點 
		{
			if(path[t2][j]!=0)
			{
				path[t1][j]=path[t2][j];
			}	
			else
			{
				path[t1][j]=t1+1;
				break;
			}
		}
		m++; 
	}

	printf("\n\n----下面為反圈法(prim法)計算結果----\n\n",ini,i+1,distance[i]);
	for(i=0;i<n;i++)
	{
		if(E[i]==1)
		{
			printf("點%d到點%d的最小距離:%d\n",ini,i+1,distance[i]);
			printf("其最短路徑:");
			for(j=0;j<n;j++)
			{
				if(path[i][j]!=0)
				{
					printf("v%d ",path[i][j]);
				}
				else break;
			}
			printf("\n");	   
		}
		else
			printf("點%d到點%d沒有路可通!\n\n",ini,i+1);
	}
}
main()
{
	inputmatrix();	
	caculate(n,matrix);
	system("pause"); 
}


針對圖2,給出執行結果:


筆者水平有限,難免有不足,請批評指正!