重新整理資料結構與演算法(c#)——演算法套路k克魯斯演算法[三十]
阿新 • • 發佈:2020-07-19
前言
這個和前面一節有關係,是這樣子的,前面是用頂點作為參照條件,這個是用邊作為參照條件。
正文
圖解如下:
每次選擇最小的邊。
但是會遇到一個小問題,就是會構成迴路。
比如說第四步中,最小邊是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; } } } }
結果: