帶權圖的鄰接矩陣中無連接的值為無限大
最小生成樹的算法:從一個頂點出發找到其他頂點的所有的邊,放入優先列隊,找到權值最小的,把它和它所到達的頂點放入樹的集合中。再以終點作為源點找到所有到其他頂點的邊(不包括已放入樹中的頂點),放入優先隊列中,再從中取最小的把它
到達的頂點放入樹的集合中(最小生成樹)。再以終點作為源點找到所有到其他頂點的邊(不包括已放入樹中的頂點),放到優先隊列中,再從中取最小的把它和它所到達的頂點放入樹的集合中,反復這樣操作到全部頂點都放入到樹中為止。
除了圖之外,我們還需要優先隊列
注意是無向有權圖的最小生成樹
優先隊列存放的是邊這個對象。
最小生成樹與最短路徑是不同的東西
最小生成樹返回的是一個頂點序列
帶權圖結構:邊,頂點,優先隊列,圖
步驟:
初始化當前頂點索引為0
{標志當前頂點正在訪問
尋找邊插入隊列(需要判斷是否存在相同終點的邊,有的話需要刪除和替換)
獲取隊列中最小的邊
輸出
改變全局變量當前頂點的值
}循環
public class Edge {//邊 public int srcVert;//存放源點的索引值 public int destVert;//存放終點的索引值 public int distance;//邊的權值 public Edge(int sv,int dv,int d) {//初始化邊 srcVert=sv; destVert=dv; distance=d; } }
public class PriorityQ {//優先隊列(優先隊列中存放的是邊對象) private final int SIZE=20;//隊列的總長度(規定邊最大的數量) private Edge[] queArray;//存放邊對象的數組 private int size;//當前邊的數量 public PriorityQ() { queArray=new Edge[SIZE]; //初始化數組 size=0;//初始化邊數量為0 } //將邊插入邊數組中 public void insert(Edge item) {//根據權值將邊插入隊列中 int j; for(j=0;j<size;j++)//循環數組,找到插入邊的位置 if(item.distance>=queArray[j].distance)//如果找到要比姚插入的邊權值大的邊,就插入該邊的后面 break;//記錄邊的位置,即j //插入(移動和插入) for(int k=size-1;k>=j;k--)//位置后移動 queArray[k+1]=queArray[k]; queArray[j]=item;//插入item邊 size++;//數據項增一 } //刪除值最小的邊 public Edge removeMin() { //因為是優先隊列,插入的時候最小值都在索引最大的地方,所以直接拿數組最后一個 return queArray[--size];//最后一個數的索引是size-1 } //刪除指定的邊(n為queArray數組的索引值) public void removeN(int n) { for(int j=n;j<size-1;j++)//從數組中刪除一個數(移動) queArray[j]=queArray[j+1]; size--; } public Edge peekMin() { return queArray[size-1]; //查看最小的邊(對應權值最小) } public int size() { return size; //當前邊的數量 } //判斷是否為空 public boolean isEmpty() { return size==0; } //查看特定的邊(n為queArray數組的索引) public Edge peekN(int n) { return queArray[n]; } //尋找特定終點的邊(findDex為邊對象的destVert屬性值) public int find(int findDex) { for(int j=0;j<size;j++) if(queArray[j].destVert==findDex) return j;//找到了就返回邊位置j return -1;//沒找到就返回-1 } }
//圖的頂點 public class Vertex { public char label;//頂點的標識符 public boolean isVisited;//頂點有無被訪問的標志 public Vertex(char lab) {//初始化頂點(屬性) label=lab; isVisited=false; } }
public class Graph { private final int MAX_VERTS=20;//最大頂點數 private final int INFINITY=1000;//無限大的值,用於表示不連通的權值 private Vertex[] vertexList;//頂點數組 private int [][]adjMat;//頂點關系的領接矩陣(鄰接矩陣的每行或者每列的位置跟頂點數組是對應的) private int nVerts;//當前頂點個數 private int currentnVert;//標志當前頂點,該值為當前頂點索引值 private PriorityQ thePQ;//優先列隊 private int nTree;//最小生成樹算法過程中,標志已訪問的頂點數量(總共需要訪問的個數是頂點的總數) public Graph() {//初始化圖 vertexList=new Vertex[MAX_VERTS]; //初始化頂點數組 adjMat=new int [MAX_VERTS][MAX_VERTS] ;//初始化鄰接矩陣 for(int j=0;j<MAX_VERTS;j++) for(int i=0;i<MAX_VERTS;i++) adjMat[i][j]=INFINITY; nVerts=0;//初始化當前頂點個數 thePQ=new PriorityQ();//建立列隊對象 } //向頂點數組中添加頂點對象(lab為頂點對象的label屬性值) public void addVertex(char lab) { vertexList[nVerts++]=new Vertex(lab);//建立lab對象,往數組內添加 } //添加邊(向鄰接矩陣中改變權值) public void addEdge(int start,int end,int weight) { //因為是無向圖所以(i,j)(j,i)都要添加1 adjMat[start][end]=weight; adjMat[end][start]=weight; } //打印頂點數組,根據獲取的頂點數組的下標值,打印頂點 public void displayVertex(int v) { System.out.print(vertexList[v].label); } //最小生成樹(輸出頂點序列) public void mstw() { currentnVert=0;//當前頂點是索引為0的頂點 while(nTree<nVerts-1) { //遍歷頂點數組,要遍歷nVerts-1次.當nTree為0的時候遍歷第一次,所以當遍歷nVerts-1次的時候,nTree為nVerts-2 vertexList[currentnVert].isVisited=true;//訪問當前頂點 nTree++;//樹中值加1(當前訪問該頂點) for(int j=0;j<nVerts;j++) {//找出當前頂點的邊(遍歷數組,是否已經訪問過,是否是連通的,是否是自己) if(j==currentnVert) continue;//如果當前比較的是自己,退出此次操作,從j+1開始 if(vertexList[j].isVisited) continue;//如果比較的是已經訪問過的,即已經加入最小生成樹的序列中,退出此次操作,從j+1開始 int distance=adjMat[currentnVert][j];//在鄰接矩陣中取當前頂點到鄰接點的邊值 if(distance==INFINITY) continue;//無連接 //如果前面都通過了,說明頂點對象數組中索引為j的是鄰接點 //放入優先隊列中(邊) 該邊的源點是一個全局變量currentnVert,不斷在發生變化 //插入有兩步,第一需要判斷隊列里面是否有以j為終點的邊,有的話需要比較刪除。第二如果沒有的話,就直接插入 putInPQ(j,distance);//邊值和該點(源點去哪了) } if(this.thePQ.size()==0) {//如果優先隊列一個值也沒有,說明沒有邊,無連接 System.out.println("圖中無連接"); return ; } Edge theEdge=thePQ.removeMin();//如果都成功了,移除隊列中最小的邊(就是我們要找的) int sourceVert=theEdge.srcVert;//最小邊的起點 currentnVert=theEdge.destVert;//最小邊的終點--下一個循環開始的源點 //打印該邊的起點和終點 System.out.print(vertexList[sourceVert].label); System.out.print(vertexList[currentnVert].label+" "); } for(int j=0;j<nVerts;j++) vertexList[j].isVisited=false; } //邊是newDist,終點是newVert放入隊列中 public void putInPQ(int newVert,int newDist) { int queueIndex=thePQ.find(newVert);//查找終點是newVert的邊 if(queueIndex!=-1) {//如果找到了(就是說如果列隊中有雖然起點不同,但是終點相同的值,就需要小的替換大的邊) Edge tempEdge=thePQ.peekN(queueIndex);//根據邊索引查詢邊 int oldDist=tempEdge.distance; if(oldDist>newDist) {//如果新的邊權值小就需要刪除舊的邊 thePQ.removeN(queueIndex);//刪除舊邊 Edge theEdge=new Edge(currentnVert,newVert,newDist);//建立新邊(新邊的起始點是一個全局變量,從一開始就設定了) thePQ.insert(theEdge);//插入新邊,不能直接替換舊邊,因為還要根據優先隊列,比較權值 } }else {//如果沒有找到 Edge theEdge=new Edge(currentnVert,newVert,newDist); thePQ.insert(theEdge); } } }
public class Test { public static void main(String[] agrs) { Graph theGraph=new Graph();//創建一個圖 theGraph.addVertex('A');//添加頂點 theGraph.addVertex('B');//添加頂點 theGraph.addVertex('C');//添加頂點 theGraph.addVertex('D');//添加頂點 theGraph.addVertex('E');//添加頂點 theGraph.addVertex('F');//添加頂點 theGraph.addEdge(0, 1,6);//添加邊 theGraph.addEdge(0, 3,4);//添加邊 theGraph.addEdge(1,2,10);//添加邊 theGraph.addEdge(1,3,7);//添加邊 theGraph.addEdge(1,4,7);//添加邊 theGraph.addEdge(2,3,8);//添加邊 theGraph.addEdge(2,4,5);//添加邊 theGraph.addEdge(2,5,6);//添加邊 theGraph.addEdge(3,4,12);//添加邊 theGraph.addEdge(4,5,7);//添加邊 theGraph.mstw(); } }