圖的連通法之普里姆算法和卡魯斯卡爾算法

最小生成樹(shù)

  • 連通圖:圖的連通其實(shí)就是樹(shù),圖的最小連通圖其實(shí)就是最小生成樹(shù)钉疫。
  • 樹(shù):如果一個(gè)無(wú)向連通圖中不存在回路豁状,則這種圖稱為樹(shù)。
  • 生成樹(shù):無(wú)向連通圖G的一個(gè)子圖如果是一顆包含G的所有頂點(diǎn)的樹(shù)鸳君,則該子圖稱為G的生成樹(shù)农渊。
  • 最小生成樹(shù):或者稱為最小代價(jià)樹(shù),對(duì)無(wú)向連通圖的生成樹(shù)或颊,各邊的權(quán)值總和稱為生成樹(shù)的權(quán)腿时,權(quán)最小的生成樹(shù)稱為最小生成樹(shù)。
最小生成樹(shù).png
最小生成樹(shù)2.png
  • 一個(gè)連通圖的生成樹(shù)是一個(gè)極小的連通子圖饭宾,它含有圖中全部的頂點(diǎn)批糟,但只有足以構(gòu)成一棵樹(shù)的n-1條邊。我們把構(gòu)造連通網(wǎng)的最小代價(jià)生成樹(shù)看铆。稱為最小生成樹(shù)徽鼎。
  • 找連通網(wǎng)的最小生成樹(shù),經(jīng)典的有兩種算法弹惦,普里姆算法克魯斯卡爾算法否淤。

普里姆算法

  • 普里姆算法:
#代表無(wú)限大


假設(shè)以v0為基準(zhǔn)開(kāi)始,探測(cè)v0到各個(gè)頂點(diǎn)的距離:
v0
0, 10, #, #, #, 11, #, #, #
看到v0到v1的距離最短為10.

接下來(lái)我們要把v1加到基準(zhǔn)里棠隐。以v0和v1為基準(zhǔn)石抡,探測(cè)到各個(gè)頂點(diǎn)的距離:
v0,v1
0, 0, 18, #, #, 11, 16, #, 12
看到到v8的舉例最短為12.
這個(gè)過(guò)程前會(huì)把v0到各個(gè)頂點(diǎn)的距離和v1到各個(gè)頂點(diǎn)的距離作比較助泽,小的留下啰扛。
=======>

v0
0, 10, #, #, #, 11, #, #, #
v1
10, 0, 18, #, #, #, 16, #, 12
可以看到相同位置的元素嚎京,18比#小,11比16小隐解,12比#小鞍帝。替換后:

v0,v1
0, 0, 18, #, #, 11, 16, #, 12

=======>

就是這樣不斷尋找基準(zhǔn)距離最短的頂點(diǎn)煞茫,將其加入基準(zhǔn)帕涌。然后再以基準(zhǔn)探測(cè)周圍舉例最短的點(diǎn)。一直到所有頂點(diǎn)都找完续徽!
圖的深度優(yōu)先遍歷.png
package com.cx.graphdemo;

public class Graph {

    private int vertexSize; // 頂點(diǎn)數(shù)量
    private int[] vertexs; // 頂點(diǎn)數(shù)組
    private int[][] matrix; // 鄰接矩陣
    private static final int MAX_WEIGHT = 1000;
    private boolean[] isVisited; // 是否被訪問(wèn)過(guò)

    public Graph(int vertexSize) {
        this.vertexSize = vertexSize;
        vertexs = new int[vertexSize];
        matrix = new int[vertexSize][vertexSize];

        for (int i = 0; i < vertexSize; i++) {
            vertexs[i] = i;
        }

        isVisited = new boolean[vertexSize];
    }

    /**
     * prim 普里姆算法
     */
    public void prim() {
        // 最小代價(jià)頂點(diǎn)權(quán)值的數(shù)組,為0表示已經(jīng)獲取最小權(quán)值
        int[] lowcost = new int[vertexSize];
        // 放頂點(diǎn)權(quán)值
        int[] adjvex = new int[vertexSize];
        int min, minId, sum = 0;

        // 把v0數(shù)組賦值給lowcost
        for (int i = 1; i < vertexSize; i++) {
            lowcost[i] = matrix[0][i];
        }

        // 只是單純的循環(huán)蚓曼,除此之外沒(méi)有任何用處
        for (int i = 1; i < vertexSize; i++) {
            min = MAX_WEIGHT;
            minId = 0;

            // lowcost數(shù)組已更新,所以還要找出lowcost中最小的元素及下標(biāo)
            for (int j = 1; j < vertexSize; j++) {
                if (lowcost[j] < min && lowcost[j] > 0) {
                    min = lowcost[j];
                    minId = j;
                }
            }

            /**
             * 為什么要找到最小元素和下標(biāo)钦扭?<br>
             * 
             * 因?yàn)樽钚≡卮懋?dāng)前的已知頂點(diǎn)到其它頂點(diǎn)的最短路徑辟躏,我們要得到這個(gè)<br>
             * 最短路徑通向的頂點(diǎn)。然后周而復(fù)始土全。<br>
             * 
             * 核心思想:<br>
             * 
             * 從某一頂點(diǎn)開(kāi)始捎琐,找到該頂點(diǎn)周圍的最短路徑及此路徑通向的頂點(diǎn)。<br>
             * 以這兩個(gè)頂點(diǎn)為準(zhǔn)裹匙,找到這兩個(gè)頂點(diǎn)周圍的最短路徑及此路徑通向的頂點(diǎn)瑞凑。<br>
             * ......最終會(huì)通過(guò)這個(gè)"最短路徑算法"走完所有的頂點(diǎn)。
             */

            // System.out.println("頂點(diǎn):" + adjvex[minId] + "權(quán)值:" + min);
            sum += min; // 加權(quán)重
            lowcost[minId] = 0;

            // 在v[minId]中找到比lowcost[]中同等位置小的值
            for (int j = 1; j < vertexSize; j++) {
                if (lowcost[j] != 0 && matrix[minId][j] < lowcost[j]) {
                    lowcost[j] = matrix[minId][j];
                    adjvex[j] = minId;
                }
            }
        }
        System.out.println("最小生成樹(shù)權(quán)值和:" + sum);
    }

    public static void main(String[] args) {
        Graph graph = new Graph(9);

        int[] a1 = new int[] { 0, 10, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 11, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT };
        int[] a2 = new int[] { 10, 0, 18, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 16, MAX_WEIGHT, 12 };
        int[] a3 = new int[] { MAX_WEIGHT, MAX_WEIGHT, 0, 22, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 8 };
        int[] a4 = new int[] { MAX_WEIGHT, MAX_WEIGHT, 22, 0, 20, MAX_WEIGHT, MAX_WEIGHT, 16, 21 };
        int[] a5 = new int[] { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 20, 0, 26, MAX_WEIGHT, 7, MAX_WEIGHT };
        int[] a6 = new int[] { 11, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 26, 0, 17, MAX_WEIGHT, MAX_WEIGHT };
        int[] a7 = new int[] { MAX_WEIGHT, 16, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 17, 0, 19, MAX_WEIGHT };
        int[] a8 = new int[] { MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 16, 7, MAX_WEIGHT, 19, 0, MAX_WEIGHT };
        int[] a9 = new int[] { MAX_WEIGHT, 12, 8, 21, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, MAX_WEIGHT, 0 };

        graph.matrix[0] = a1;
        graph.matrix[1] = a2;
        graph.matrix[2] = a3;
        graph.matrix[3] = a4;
        graph.matrix[4] = a5;
        graph.matrix[5] = a6;
        graph.matrix[6] = a7;
        graph.matrix[7] = a8;
        graph.matrix[8] = a9;

        graph.prim();
    }
}

克魯斯卡爾算法

克魯斯卡爾算法.png
普里姆算法是按頂點(diǎn)來(lái)連通圖概页,而克魯斯卡爾算法則是按邊來(lái)構(gòu)成圖籽御。

兩個(gè)頂點(diǎn)確立一條邊,沒(méi)有問(wèn)題惰匙!所有的邊可以從小到大排序技掏,也沒(méi)有問(wèn)題!
克魯斯卡爾算法就是按照這個(gè)順序排好的邊來(lái)連通圖项鬼。

規(guī)則:
1.找到最小的邊哑梳。
2.探測(cè)邊的兩個(gè)頂點(diǎn)是否會(huì)構(gòu)成回環(huán)。
3.如果構(gòu)成回環(huán)則放棄這條邊绘盟,尋找下一條鸠真。
4.如果不構(gòu)成回環(huán),則記錄龄毡。再尋找下一條吠卷。

整個(gè)尋找過(guò)程,可以看下圖:一點(diǎn)點(diǎn)畫的沦零,一定能看懂祭隔!

注意:這里面有一個(gè)點(diǎn)。探測(cè)是否會(huì)構(gòu)成回環(huán)路操!克魯斯卡爾用一個(gè)神奇的數(shù)組來(lái)完成這個(gè)探索疾渴!
比如:
v4-v7千贯,權(quán)重為7,是最小的邊程奠。則記錄4號(hào)元素為7。也就是說(shuō):以起始點(diǎn)為下標(biāo)祭钉,以結(jié)束點(diǎn)為值瞄沙!

[0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

v2-v8,權(quán)重為8
[0, 0, 8, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

如果這時(shí)候第3條為:
v2-v1慌核,權(quán)重為5距境。這不就沖突了嗎?因?yàn)?號(hào)位置上已經(jīng)有元素了垮卓。如果發(fā)生這種情況垫桂,就以該位置上值為位置放置新頂點(diǎn)。也就是:
[0, 0, 8, 0, 7, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]

為什么這樣做粟按?因?yàn)檫@是最簡(jiǎn)單的測(cè)試回環(huán)的方法诬滩。
2號(hào)位置是8,代表v2和v8相連灭将。8號(hào)位置是1疼鸟,代表v8和v1相連。
如果再有邊是v1-v2庙曙,那么就構(gòu)成了回環(huán)空镜,一個(gè)三角回環(huán)。所以理解這個(gè)數(shù)組很重要捌朴!
克魯斯卡爾算法過(guò)程.png
package com.cx.graphtraversal;

public class GraphKruskal {

    private Edge[] edges;
    private int edgeSize; // 邊的數(shù)量

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

    public void miniSpanTreeKruskal() {
        int m, n, sum = 0;
        int[] parent = new int[edgeSize]; // 神奇的數(shù)組吴攒,下標(biāo)為起點(diǎn),值為終點(diǎn)
        for (int i = 0; i < edgeSize; i++) {
            parent[i] = 0;
        }
        for (int i = 0; i < edgeSize; i++) {
            n = find(parent, edges[i].begin);
            m = find(parent, edges[i].end);
            if (n != m) { // 代表沒(méi)有回環(huán)
                parent[n] = m;
                System.out.println("起始頂點(diǎn):" + edges[i].begin + "---結(jié)束頂點(diǎn):" + edges[i].end + "~權(quán)值:" + edges[i].weight);
                sum += edges[i].weight;
            } else {
                System.out.println("第" + i + "條邊回環(huán)了");
            }
        }
        System.out.println("sum:" + sum);
    }

    /**
     * 將神奇數(shù)組進(jìn)行查詢獲取非回環(huán)的值
     */
    public int find(int[] parent, int f) {
        while (parent[f] > 0) {
            System.out.println("找到起點(diǎn)" + f);
            f = parent[f];
            System.out.println("找到終點(diǎn):" + f);
        }
        return f;
    }

    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 {
        private int begin;
        private int end;
        private int weight;

        public Edge(int begin, int end, int weight) {
            super();
            this.begin = begin;
            this.end = end;
            this.weight = 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;
        }
    }

    public static void main(String[] args) {
        GraphKruskal graphKruskal = new GraphKruskal(15);
        graphKruskal.createEdgeArray();
        graphKruskal.miniSpanTreeKruskal();
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末砂蔽,一起剝皮案震驚了整個(gè)濱河市洼怔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌左驾,老刑警劉巖茴厉,帶你破解...
    沈念sama閱讀 222,865評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異什荣,居然都是意外死亡矾缓,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,296評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門稻爬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)嗜闻,“玉大人,你說(shuō)我怎么就攤上這事桅锄×瘀ǎ” “怎么了样眠?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,631評(píng)論 0 364
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)翠肘。 經(jīng)常有香客問(wèn)我檐束,道長(zhǎng),這世上最難降的妖魔是什么束倍? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,199評(píng)論 1 300
  • 正文 為了忘掉前任被丧,我火速辦了婚禮,結(jié)果婚禮上绪妹,老公的妹妹穿的比我還像新娘甥桂。我一直安慰自己,他們只是感情好邮旷,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,196評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布黄选。 她就那樣靜靜地躺著,像睡著了一般婶肩。 火紅的嫁衣襯著肌膚如雪办陷。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,793評(píng)論 1 314
  • 那天律歼,我揣著相機(jī)與錄音懂诗,去河邊找鬼。 笑死苗膝,一個(gè)胖子當(dāng)著我的面吹牛殃恒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播辱揭,決...
    沈念sama閱讀 41,221評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼离唐,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了问窃?” 一聲冷哼從身側(cè)響起亥鬓,我...
    開(kāi)封第一講書(shū)人閱讀 40,174評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎域庇,沒(méi)想到半個(gè)月后嵌戈,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,699評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡听皿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,770評(píng)論 3 343
  • 正文 我和宋清朗相戀三年熟呛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片尉姨。...
    茶點(diǎn)故事閱讀 40,918評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡庵朝,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情九府,我是刑警寧澤椎瘟,帶...
    沈念sama閱讀 36,573評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站侄旬,受9級(jí)特大地震影響肺蔚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜儡羔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,255評(píng)論 3 336
  • 文/蒙蒙 一宣羊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧笔链,春花似錦段只、人聲如沸腮猖。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,749評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)澈缺。三九已至坪创,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間姐赡,已是汗流浹背莱预。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,862評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留项滑,地道東北人依沮。 一個(gè)月前我還...
    沈念sama閱讀 49,364評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像枪狂,于是被迫代替她去往敵國(guó)和親危喉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,926評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容