最小生成樹表示得是連通圖的極小連通子圖,它包含全部的的頂點,但足以生成n-1條邊的數。算法
下面是我學習的內容和理解。數組
1.使用普里姆算法構成最小生成樹。數據結構
先講一下普里姆算法的思路。普里姆算法思路是這樣的:學習
前提:G={V,E} 這個是咱們圖的定義這個應該是明白啥意思的。ui
1.如今定義一個V1表示一個空的新集合this
2.首先隨機的將V中的一個節點放入到V1中,做爲遍歷圖開始位置spa
3.其次選取V1中的一個節點和選取V中的一個節點,使邊的權值是E中最小的(必須是存在E中的,否則不是兩個節點不是連通的,也沒用意義。第一次從V1只能選取咱們隨機取的那個節點啦,可是後面須要看狀況)code
4.再將V中的那個節點放入到V1中來,而且將咱們剛纔在第三3步使用過的那邊條從E中刪除,重複第3步,只到V1=V.blog
下面是JAVA實現:get
private int edgeSize; private int vertexeSize; public static final int MAX_VALUE = Integer.MAX_VALUE; private List<Edge> minimumTreeEdges = new ArrayList<>(); //最小生成樹 n-1 private int[][] arc = new int[9][9]; //邊集合 鄰接矩陣表示方式 /** * 普里姆構建最小生成樹 */ public void buildMinimumTreeByPrim() { int[] minimumLine = new int[vertexeSize]; int[] minimumLine2LastSearched = new int[vertexeSize]; //這裏是記錄上一次最小權值節點的下標 for (int i = 1; i < vertexeSize; i++) { minimumLine[i] = arc[0][i]; //首先將第一排的數據做爲最小的一列值後面將從這其中查找最小權值 minimumLine2LastSearched[i] = 0; } minimumLine[0] = 0; //0已經在加入到了遍歷過的節點中去 minimumLine2LastSearched[0] = 0;//表示0是0的上一次的最小權值(由於0是開始,因此前面仍是0了) for (int i = 1; i < vertexeSize; i++) { int k = 0, min = MAX_VALUE; for (int j = 1; j < vertexeSize; j++) { if (minimumLine[j] != 0 && minimumLine[j] < min) { //這裏是找目前全部的邊中權值最小的那個 min = minimumLine[j]; k = j; } } // NotBoundEdge edge = new NotBoundEdge(minimumLine2LastSearched[k], k); //若是是第一次的話 爲0 由於由0這個最小權值計算出了k這個最小權值 // minimumTreeEdges.add(edge); System.out.println("("+minimumLine2LastSearched[k]+","+k+")"); minimumLine[k] = 0; //由於這個k已經遍歷過了 因此設置爲0 for (int j = 0; j < vertexeSize; j++) { if (minimumLine[j] != 0 && arc[k][j] < minimumLine[j]) { minimumLine[j] = arc[k][j]; //若是有比其餘更小權值得節點 那麼就覆蓋(更大的權值已經沒有任何意義了) minimumLine2LastSearched[j] = k; //這裏就表示k是j的上一個節點(若是權值最小 也就是: (k,j)) } } } }
上面不是我本身實現的,上面的思路是網上最爲流行的,因此就將這種算法理解了一下(把變量名改了一下,之前的i,j,k真是不能忍),而後加了一些本身的理解.我以爲這個算法很很差理解的地方在於
int[] minimumLine = new int[vertexeSize]; int[] minimumLine2LastSearched = new int[vertexeSize]; //這裏是記錄上一次最小權值節點的下標
這兩個數組,由於的代碼基本都沒怎麼講明白(可能恰好我看的幾遍都沒講清楚),因此我就加上本身的註釋了,linimumLine 一開始記錄的和0相關的權值,可是到了後來就不是了,是記錄了每一行只要比它對應的那一列小的權值.
2 克魯斯卡爾算法
這個算法的原理是這樣的:
前提:G={V,E}
1.先定義 一個T={V1,{}},初始化的時候,T中是沒有邊的,只有n個頂點,圖中的每個節點都做爲一個連通份量
2.在E中選擇代價最小的邊,若邊依附於不用的連通份量,那麼就將這個邊放到T中,不然就丟棄這條邊,一直到T中的全部的幾點都在一個連通份量上
代碼:
private NetNode[] netNodes=new NetNode[15];
/** * TODO * 根據克魯斯卡爾構建最小生成樹 */ public void buildMinimumByKruskal() { int[] parent=new int[edgeSize]; for (int i=0;i<vertexeSize;i++){ parent[i]=0; } int m,n; for (int i=0;i<vertexeSize;i++){ /** * 若是這裏求出的n=m的話 那麼就表示構成環了( * 由於parent[index]>0的話 就會接着往下走,直到parent[index]=0. * 這條路線能夠當作是邊連在一塊兒的. * 這裏就至關於從begin 和end 這兩個點開始往下一直連線[直到parent[index]=0結束]. * 若是碰到了一塊(m=n) 那麼就說明他們相遇了 * ) */ m=findNode(parent,netNodes[i].getBegin()); n=findNode(parent,netNodes[i].getEnd()); if(m!=n){ parent[n]=m; System.out.println("("+netNodes[i].begin+","+netNodes[i].getEnd()+")"); } } } /** * 這裏比較難理解 * 1.這裏其實就是想查看是否在用一個子集(若是parent[index]=0 * 那就也就表示沒有連在一塊兒) * @param parent * @param index * @return */ public int findNode(int[] parent, int index) { while (parent[index] > 0) { index = parent[index]; } return index; } public interface Edge { int getBegin(); int getEnd(); int getWeight(); } /** * */ public class NetNode implements Edge { private int begin; private int end; private int weight; public int getBegin() { return begin; } public void setBegin(int begin) { this.begin = begin; } public int getEnd() { return end; } public void setEnd(int end) { this.end = end; } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } }
注意我這裏沒體現出的一個地方是:放進去的netNodes是按照權值從小到大的:
參考:
大話數據結構 第七章