1. 程式人生 > >Kruskal演算法介紹與實現

Kruskal演算法介紹與實現

最小生成樹MinimumSpanning Tree,MST)或者稱為最小代價生成樹:對無向連通圖的生成樹,各邊的全值總和稱為生成樹的權,權最小的生成樹稱為最小生成樹。

構造最小生成樹的準則有三條:

1)必須只使用該網路中的邊來構造最小生成樹;

2)必須使用且僅使用n-1條邊來連線網路中的n個頂點;

3)不能使用產生迴路的邊。

構造最小生成樹的演算法主要有:克魯斯卡爾(Kruskal)演算法、Boruvka演算法、和普里姆(Prim)演算法,他們均遵循以上準則,且都採用了一種逐步求解的策略。

Kruskal演算法的基本思想是以邊為主導地位,始終都是選擇當前可用的最小權值的邊,步驟如下:

1)設一個有n個頂點的連通網路為GV,E),最初先構造一個只有n個頂點,沒有邊的非連通圖T{V,Ø},圖中每個頂點自成一個連通分量;

2)當在E中選擇一條具有最小權值的邊時,若該邊的兩個頂點落在不同的連通分量上,則將此邊加入到T中;否則,即這條邊的兩個頂點落在同一個連通分量上,則將此邊捨去(此後永不選用這條邊),重新選擇一條權值最小的邊;

3)如此重複下去,直到所有頂點在同一個連通分量上為止。

Kruskal演算法實現

以下圖無向網為例:


利用Kruskal演算法求上圖所示的無向網的最小生成樹,並輸出依次選擇的各條邊及最終求得的最小生成樹的權。資料輸入格式:第一行為頂點個數n和邊數

m,第二行開始為每條邊的資料:u,v,w,分別表示這條邊的兩個頂點及邊上的權值。頂點序號從1開始計起。

解:Code

#include<stdio.h>
#include<stdlib.h>
#include<iostream>
using namespace std;

#define MAXN 11		//頂點個數的最大值
#define MAXM 20		//邊的個數的最大值
struct edge			//邊
{
	int u, v, w;
}edges[MAXM];		//邊的陣列
int parent[MAXN];	//parent[i]為頂點i所在集合對應的樹中的根結點
int n, m;			//頂點個數、邊的個數
int i, j;			//迴圈變數
void UFset()		//初始化 
{
	for(i = 1; i <= n; i++) parent[i] = -1;
}
int Find(int x)		//查詢並返回結點x所屬集合的根結點
{
	int s;			//查詢位置
	for(s = x; parent[s] >=0; s = parent[s]) ;
	while(s != x)	//優化方案——壓縮路徑,使後續的查詢操作加速
	{
		int tmp = parent[x];
		parent[x] = s;
		x = tmp;
	}
	return s;
}
//運用並查集,將兩個不同集合的元素進行合併,使兩個集合中任意兩個元素都連通
void Union(int R1, int R2)
{
	int r1 = Find(R1), r2 = Find(R2);		//r1和r2分別為R1和R2的根結點
	int tmp = parent[r1] + parent[r2];		//兩個集合結點數之和(負數)
	//如果R2所在樹結點個數 > R1所在樹結點個數(注意parent[r1]是負數)
	if(parent[r1] > parent[r2])
	{
		parent[r1] = r2;
		parent[r2] = tmp;
	}
	else
	{
		parent[r2] = r1;
		parent[r1] = tmp;
	}
}
int cmp(const void *a, const void *b)		//實現從小到大的比較函式
{
	edge aa = *(const edge *)a, bb = *(const edge *)b;
	return aa.w-bb.w;
}
void Kruskal()
{
	int sumweight = 0;		//生成樹的權值
	int num = 0;			//已選用的邊的數目
	UFset();				//初始化parent陣列
	for(i = 0; i < m; i++)
	{
		if(Find(edges[i].u) != Find(edges[i].v))
		{
			printf("%d %d %d\n", edges[i].u, edges[i].v, edges[i].w);
			sumweight += edges[i].w; num++;
			Union(edges[i].u, edges[i].v);
		}
		if(num >= n-1) break;
	}
	printf("The weight of MST is : %d\n", sumweight);
}
void main()
{
	scanf("%d%d", &n, &m);	//讀入頂點個數和邊數
	for(int i = 0; i < m; i++)
		scanf("%d%d%d", &edges[i].u, &edges[i].v, &edges[i].w);	//讀入邊的起點和終點
	printf("The edges chosen are :\n");
	qsort(edges, m, sizeof(edges[0]), cmp);	//對邊按權值從小到大排序
	Kruskal();
}
執行結果: