圖(帶權無向圖)最小生成樹


帶權圖的鄰接矩陣中無連接的值為無限大
最小生成樹的算法:從一個頂點出發找到其他頂點的所有的邊,放入優先列隊,找到權值最小的,把它和它所到達的頂點放入樹的集合中。再以終點作為源點找到所有到其他頂點的邊(不包括已放入樹中的頂點),放入優先隊列中,再從中取最小的把它
到達的頂點放入樹的集合中(最小生成樹)。再以終點作為源點找到所有到其他頂點的邊(不包括已放入樹中的頂點),放到優先隊列中,再從中取最小的把它和它所到達的頂點放入樹的集合中,反復這樣操作到全部頂點都放入到樹中為止。
除了圖之外,我們還需要優先隊列
注意是無向有權圖的最小生成樹
優先隊列存放的是邊這個對象。
最小生成樹與最短路徑是不同的東西
最小生成樹返回的是一個頂點序列

帶權圖結構:邊,頂點,優先隊列,圖
步驟:
初始化當前頂點索引為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();
        
    }

}

 


免責聲明!

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



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