圖的克魯斯卡爾算法求最小生成樹


克魯斯卡爾算法的核心思想是:在帶權連通圖中,不斷地在邊集合中找到最小的邊,如果該邊滿足得到最小生成樹的條件,就將其構造,直到最后得到一顆最小生成樹。

       克魯斯卡爾算法的執行步驟:
       第一步:在帶權連通圖中,將邊的權值排序(從小到大);
       第二步:判斷是否需要選擇這條邊(此時圖中的邊已按權值從小到大排好序)。判斷的依據是邊的兩個頂點是否已連通,如果連通則繼續下一條;如果不連通,那么就選擇使其連通。
       第三步:循環第二步,直到圖中所有的頂點都在同一個連通分量中,即得到最小生成樹。

下面我用圖示法來演示克魯斯卡爾算法的工作流程,如下圖:

 

首先,將圖中所有的邊排序(從小到大),我們將以此結果來選擇。排序后各邊按權值從小到大依次是:

HG < (CI=GF) < (AB=CF) < GI < (CD=HI) < (AH=BC) < DE < BH < DF

並且定義一個parent[]數組,大小為圖中頂點個數,初始化{0,0,0,0,0,0,0,0,0},下標對應的值是當前頂點的終結點。

H到G:1、C到I:2、G到F:2、A到B:4、C到F:4、G到I:6、C到D:7、H到I:7、A到H:8、B到C:8、D到E:9、B到H:11、D到F:14

需要注意的是:我們把上面左邊左邊定位begin,右邊頂點定為end,例如HG:H begin G end

接下來,我們先選擇HG邊,將這兩個點加入到已找到點的集合。這樣圖就變成了,如圖
注意:當我們把HG連接起來后,因為此時H和G都沒有他們的最終端頂點(因為parent里此時都是0,0代表沒有終頂點),所以可以連接,並且把H的parent設為G!
parent={0,0,0,0,0,0,0,6,0},意思是H頂點的終頂點是6下標(G)了
繼續,這次選擇邊CI(當有兩條邊權值相等時,可隨意選一條),此時需做判斷。

判斷法則:當將邊CI加入到已找到邊的集合中時,是否會形成回路?
      1.我們首先要判斷C和I這倆頂點分別的終頂點,C(parent對應的2下標),I(對應8下標),都是0,所以沒有回環,可以連
      2.如果C和I倆終頂點是一樣的,形成回路,不符合要求,直接進行下一次操作
而此時C和I對應的都是0,所以都沒有終頂點,可連,並且把C的終頂點設為I
parent={0,0,8,0,0,0,0,6,0}

繼續,選擇GF邊,對G和F分別判斷終頂點是誰,因為parent數組下標5和6都是0,所以沒有回環,可以連

parent={0,0,8,0,0,0,5,6,0},意思是G頂點的終頂點是5下標(F)了

繼續,選擇AB進行判斷,因為parent數組下標0,1都是0,所以不回環,可以連接

 

parent={1,0,8,0,0,0,5,6,0},意思是G頂點的終頂點是5下標(F)了

繼續,對CF進行判斷,注意了parent數組下標是2對應的值是8(I),但是8下標對應的值是0,也就是說目前C的終頂點是I,F對應的下標5對應的值是0,所以他倆終點不一樣,可以連接!

同時,把I的終頂點設為F

parent={1,0,8,0,0,0,5,6,5},意思是I頂點的終頂點是5下標(F)了

繼續,這次選擇GI,注意了parent數組下標6對應的值是5,8下標的值是5,所以形成了回環,不可連!

繼續,選擇CD,C和D對應的終頂點是F和無,所以CD可以連!同時把C的終頂點F的終頂點設為D

parent={1,0,8,0,0,3,5,6,5},意思是F頂點的終頂點是3下標(D)了

繼續對邊集合所有的邊按照上面的規則判斷,得到最終的最小生成樹是:

 代碼如下:

public class GraphKruskal2 {
	public Edge[] edges;
	public int edgeSize;;

	public GraphKruskal2(int edgeSize) {
		this.edgeSize = edgeSize;
		edges = new Edge[edgeSize];
	}

	public void createEdgeArray() {
		Edge edge0 = new Edge(4, 7, 7);
		Edge edge1 = new Edge(2, 8, 8);
		Edge edge2 = new Edge(0, 1, 10);
		Edge edge3 = new Edge(0, 5, 11);
		Edge edge4 = new Edge(1, 8, 12);
		Edge edge5 = new Edge(3, 7, 16);
		Edge edge6 = new Edge(1, 6, 16);
		Edge edge7 = new Edge(5, 6, 17);
		Edge edge8 = new Edge(1, 2, 18);
		Edge edge9 = new Edge(6, 7, 19);
		Edge edge10 = new Edge(3, 4, 20);
		Edge edge11 = new Edge(3, 8, 21);
		Edge edge12 = new Edge(2, 3, 22);
		Edge edge13 = new Edge(3, 6, 24);
		Edge edge14 = new Edge(4, 5, 26);
		edges[0] = edge0;
		edges[1] = edge1;
		edges[2] = edge2;
		edges[3] = edge3;
		edges[4] = edge4;
		edges[5] = edge5;
		edges[6] = edge6;
		edges[7] = edge7;
		edges[8] = edge8;
		edges[9] = edge9;
		edges[10] = edge10;
		edges[11] = edge11;
		edges[12] = edge12;
		edges[13] = edge13;
		edges[14] = edge14;
	}

	class Edge {
		public int begin;
		public int end;
		public int weight;

		public Edge(int begin, int end, int weight) {
			this.begin = begin;
			this.end = end;
			this.weight = weight;
		}

	}

	public void kruskal() {
		int m, n, sum = 0;
		int[] parent = new int[edgeSize];// 下標指頂點位置,值指的是當前下標對應的頂點鄰接父節點
		for (int i = 0; i < edgeSize; i++) {
			parent[i] = 0;
		}
		for (int i = 0; i < edgeSize; i++) {
			// 對邊數組進行從小到大遍歷
			// 找到邊的begin,判斷當前begin的終頂點
			n = find(parent, edges[i].begin);
			// 找到邊的end,判斷當前end的終頂點
			m = find(parent, edges[i].end);
			// 如果當前n=m的話,就代表有回環,不能連接
			if (n != m) {
				parent[n] = m;
				System.out.println("起始頂點:" + edges[i].begin + " ---結束頂點:"
						+ edges[i].end);
				sum += edges[i].weight;
			} else {
				System.out.println("第" + i + "條邊回環了");
			}
		}
		System.out.println("權值總和:" + sum);
		printArray(parent);
	}

	private int find(int[] parent, int f) {
		// 找到當前f節點的父節點的父節點.....(以此類推)的下標
		while (parent[f] > 0) {
			System.out.println("找到起點:" + f);
			f = parent[f];
			System.out.println("找到終點:" + f);
		}
		return f;// 返回的這個代表當前f暫時還沒有了父節點
	}

	// (3)
	public static void printArray(int[] array) {
		System.out.print("{");
		for (int i = 0; i < array.length; i++) {
			System.out.print(array[i]);
			if (i < array.length - 1) {
				System.out.print(", ");
			}
		}
		System.out.println("}");
	}

	public static void main(String[] args) {
		GraphKruskal2 kruskal = new GraphKruskal2(15);
		kruskal.createEdgeArray();
		kruskal.kruskal();
	}
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM