圖的連通性---最小生成樹(Prim算法)

1.如今要選擇這樣一個生成樹,也就是使總耗費最少。這個問題就是構造連通網的最小代價生成樹(Minimum Cost Spanning Tree)簡稱爲最小生成樹的問題。一顆生成樹的代價就是樹上各邊的代價之和。java

構造最小生成樹的算法有多種。大多算法利用了最小生成樹的下列簡稱爲MST的特性:算法

假設N=(V,{E})是一個連通網,(N表明連通網,V表明網的各個頂點,E表明網的邊),U是頂點集V的一個非空子集。若(u,v)是一條具備最小權值的邊,其中u屬於U,v屬於V-U,則必存在一顆包含邊(u,v)的最小生成樹。數組

2.反證法證實上面MST特性:code

假設網N的任意一顆最小生成樹都不包含(u,v).設T是連通網上的一顆最小生成樹,當將邊(u,v)加入T中時,由生成樹的定義可知,T中必存在一條包含(u,v)的迴路。排序

另外一方面,因爲T是生成樹,則在T中必存在一條(u1,v1),其中u1屬於U,v1屬於V-U,且u,u1,v,v1必有路徑相通。刪去(u1,v1)即可消除上述迴路,同時獲得另外一顆生成樹T1。因爲(u,v)的代價不高於(u1,v1),則T1的代價不高於T,T1是包含(u,v)的一顆最小生成樹。由此可假設矛盾。索引

3.普里姆(prim)算法 get

假設N=(V,{E})是連通網,TE是N上最小的生成樹中邊的集合。算法從U={u0},(u0屬於V),TE={}開始,重複執行下述操做:在全部u屬於U,v屬於V-U的邊(u,v)屬於E中,尋找一條代價最小的邊(u0,v0)併入集合TE中,同時v0也併入集合U中,直到U=V爲止。此時TE中必有n-1條邊,而T=(V,{E})爲N的最小生成樹。io

爲實現這個算法,須要附設一個輔助數組closedge,以記錄從U到U-V具備最小代價的邊。對每一個頂點vi屬於V-U,在輔助數組中存在一個相應份量closedge[i-1],它包含兩個域,其中lowcost存儲該邊上的權。vex存儲附屬在該邊上的在U中的頂點。 class

/**
     * 普里姆算法,最小生成樹,
     * 從圖中第start個元素開始,生成最小樹
     */
    public void prim(int start) {
        Integer vexNum = mVexs.length; //頂點的個數
        int index = 0;//prim最小樹的索引,即prims數組的索引
        String[] prims = new String[vexNum]; //prim最小樹的結果數組
        int[] weights = new int[vexNum]; //頂點間的權值
        prims[index++] = mVexs[start].data; //prim生成樹的第一個頂點是從start開始的

        /**
         * 初始化頂點的權值數組;
         * 將每一個頂點的權值初始化爲第start個頂點到該頂點的權值
         */
        for (int i = 0; i < vexNum; i++) {
            weights[i] = getWeight(start, i);
        }


        for (int i = 0; i < vexNum; i++) {
            if (start == i) {
                continue; //終止本次循環,進行下一次循環,break終止for中的循環,進行循環下面的語句
            }
            int j = 0, k = 0, min = INF;
            //在未被加入最小生成樹的頂點中,找出權值最小的頂點
            while (j < vexNum) {
                //當weights[j]=0時,意味着第j個頂點已經被排序過,或者說已經加入了最小生成樹中
                if (weights[j] != 0 & weights[j] < min) {
                    min = weights[j];
                    k = j;  //將最小權值的頂點賦給k
                }
                j++;
            }

            /**
             *  通過上面的while循環,則知在未被加入到最小生成樹的頂點中,權值最小的是k
             *  將第k個頂點加入到prims數組中
             */
            prims[index++] = mVexs[k].data;
            //並將第k個頂點的權值標記爲0,意味着第k個頂點已經被排序過了或者說被加入到了最小生成樹的結果中
            weights[k] = 0;

            //更新其餘頂點到最小生成樹中頂點的權值
            for (j = 0; j < vexNum; j++) {
                //獲取第k個頂點到第j個頂點的權值
                int tmp = getWeight(k, j);
                //當第j個頂點未在最小生成樹中,且第j個頂點到最小生成樹中頂點的權值須要更新時,即加入k頂點後,已再也不是最小權值時
                if (weights[j] != 0 && tmp < weights[j]) {
                    weights[j] = tmp;
                }
            }
        }
        /**
         * 當prim最小生成樹已經生成完畢時
         * 計算最小生成樹的權值
         */
        int sum = 0;
        //要從第2個元素開始,索引爲1,由於要計算兩頂點之間的權值
        for (int i = 1; i < index; i++) {
            int min = INF;
            //或者去prims[i]頂點在頂點中的位置
            int n = getPostion(prims[i]);
            for (int j = 0; j < i; j++) {
                int m = getPostion(prims[j]);
                int tmp = getWeight(m, n);
                if (tmp < min) {
                    min = tmp;

                }
            }
            sum += min;

        }


        /**
         * 打印最小生成樹
         */
        System.out.println("PRIM" + mVexs[start].data + "=" + sum);
        for (int i = 0; i < index; i++) {
            System.out.println(prims[i]);

        }
    }
相關文章
相關標籤/搜索