1. 程式人生 > 實用技巧 >重新整理資料結構與演算法(c#)——演算法套路k克魯斯演算法[三十]

重新整理資料結構與演算法(c#)——演算法套路k克魯斯演算法[三十]

前言

這個和前面一節有關係,是這樣子的,前面是用頂點作為參照條件,這個是用邊作為參照條件。

正文

圖解如下:

每次選擇最小的邊。

但是會遇到一個小問題,就是會構成迴路。

比如說第四步中,最小邊是CE,但是沒有選擇CE,因為CE會形成迴路。

那麼如何判斷是否有迴路呢?

判斷兩個點的終點,是否一致。

這個是怎麼來的呢?為什麼判斷終點是否一致就可以判斷有迴路呢?

是這樣子的,如何兩個點可以共同到達任何一個點,那麼他們之間一定是通的,這一點是肯定的,如果他們本來就是通的,再在他們之間加一條那麼肯定迴路的。

那麼為什麼選擇終點呢?是這樣子的,假如他們之間選來不相通,他們肯定在兩條路上。

假設選了cd和ef這兩條路。那麼他們這兩條路的終點分別是d(c->d)和f(e->f),他們的終點不同,那麼他們沒有在一條路上,所以把c->d的終點d的終點設定為e->另一條路的終點也就是f,連線後所有節點都有公共的終點,那麼終點就可以作為判斷依據,這樣就不用去遍歷了。

程式碼如下:

private static int INF = int.MaxValue;

static void Main(string[] args)
{
	char[] vertexs = { 'A', 'B', 'C', 'D', 'E', 'F', 'G' };
	//克魯斯卡爾演算法的鄰接矩陣  
	int[,] matrix = {
  /*A*//*B*//*C*//*D*//*E*//*F*//*G*/
/*A*/ {   0,  12, INF, INF, INF,  16,  14},
/*B*/ {  12,   0,  10, INF, INF,   7, INF
},
/*C*/ { INF,  10,   0,   3,   5,   6, INF},
/*D*/ { INF, INF,   3,   0,   4, INF, INF},
/*E*/ { INF, INF,   5,   4,   0,   2,   8},
/*F*/ {  16,   7,   6, INF,   2,   0,   9},
/*G*/ {  14, INF, INF, INF,   8,   9,   0}};
	KruskaCase kruskaCase = new KruskaCase(vertexs,matrix);
	kruskaCase.kruskal();
	Console.ReadKey();
} 
}

public class KruskaCase
{
private int edgeNum;//邊的個數

private char[] vertexs;//頂點陣列

private int[,] matrix;//鄰接矩陣

private static int INF = int.MaxValue;

public KruskaCase(char[] vertexs, int[,] matrix)
{
	int vlen = vertexs.Length;
	//初始化頂點,避免汙染資料
   this.vertexs = new char[vlen];
	for (int i=0;i<vlen;i++)
	{
		this.vertexs[i] = vertexs[i];
	}

	//初始化邊, 避免汙染資料
	this.matrix = new int[vlen,vlen];
	for (int i = 0; i < vlen; i++)
	{
		for (int j = 0; j < vlen; j++)
		{
			this.matrix[i,j] = matrix[i,j];
		}
	}
	for (int i = 0; i < vlen; i++)
	{
		for (int j = i+1; j < vlen; j++)
		{
			if (matrix[i, j] != INF)
			{
				edgeNum++;
			}
		}
	}
}

public void kruskal()
{
	//表示第幾條邊加入最後的結果
	int index = 0;
	//記錄每個頂點的終點
	int[] ends = new int[edgeNum];
	//最後邊的個數肯定是節點數量-1
	EData[] result = new EData[vertexs.Length-1];
	EData[] edges = getEData();
	sortEData(edges);
	for (int i=0;i<edgeNum;i++)
	{
		var start = getPosition(edges[i].start);
		var end = getPosition(edges[i].end);
		int startEndPoint = getEnd(ends,start);
		int endEndPoint = getEnd(ends, end);
		if (startEndPoint != endEndPoint)
		{
			ends[startEndPoint] = endEndPoint;
			result[index++] = edges[i];
		}
	}
	Console.WriteLine("最小生成樹為:");
	for (int i=0;i<result.Length;i++)
	{
		Console.WriteLine(result[i]);
	}
}

private  int getPosition(char ch)
{
	for (int i = 0; i < vertexs.Length; i++)
	{
		if (vertexs[i] == ch)
		{
			return i;
		}
	}
	return -1;
}

private int getEnd(int[] ends, int i)
{ // i = 4 [0,0,0,0,5,0,0,0,0,0,0,0]
	while (ends[i] != 0)
	{
		i = ends[i];
	}
	return i;
}

/// <summary>
/// 對邊陣列進行排序
/// </summary>
/// <param name="edges">需要排序的邊陣列</param>
private void sortEData(EData[] edges)
{
	for (int i = 0; i < edges.Length - 1; i++)
	{
		for (int j = 0; j < edges.Length - 1 - i; j++)
		{
			if (edges[j].weight > edges[j + 1].weight)
			{//交換
				EData tmp = edges[j];
				edges[j] = edges[j + 1];
				edges[j + 1] = tmp;
			}
		}
	}
}

結果: