圖的相關(guān)算法(一):廣度和深度優(yōu)先遍歷沙咏、拓?fù)渑判?/h1>

上一次我們介紹了圖的相關(guān)的數(shù)據(jù)結(jié)構(gòu)辨图,今天我們來介紹圖的有關(guān)算法。
接下來我將以如下的順序介紹算法:1.圖的遍歷(廣度和深度)外帶解決拓?fù)渑判? 2.最小生成樹 3.最短路徑

一肢藐、圖的遍歷

1.基本思路

1).圖的遍歷:從圖中某一個頂點出發(fā)遍歷途中其余結(jié)點故河,每一 個頂點僅僅被遍歷 一次。
2).基本思路
    (1)樹有四種遍歷方式吆豹,因為根結(jié)點只有一個鱼的。而圖的復(fù)雜情況是順著一個 
       點向下尋找,很有可能又找回到自己痘煤,容易形成回路造成死循環(huán)凑阶。
    (2)所以要設(shè)置一個數(shù)組visited[n],n是圖中頂點的個數(shù)衷快,初值為0宙橱,當(dāng)該頂  
       點被遍歷后,修改數(shù)組元素值為1.
    (3)基于上面的思想烦磁,形成兩個遍歷方案:深度優(yōu)先遍歷和廣度優(yōu)先遍歷养匈。

2.深度優(yōu)先遍歷

深度優(yōu)先遍歷,就是從初始訪問結(jié)點出發(fā)都伪,我們知道初始訪問結(jié)點可能有多個鄰接結(jié)點呕乎,深度優(yōu)先搜索的策略是首先訪問第一個鄰接結(jié)點陨晶,再以這個被訪問的鄰接結(jié)點作為初始結(jié)點帝璧,訪問它的第一鄰接結(jié)點湿刽〉乃福總結(jié)來說:每次訪問當(dāng)前結(jié)點后首先訪問當(dāng)前結(jié)點的第一個鄰接結(jié)點。
這種策略是優(yōu)先縱向深度诈闺,而不是對一個結(jié)點的鄰接結(jié)點做橫向訪問渴庆。算法標(biāo)識如下:

(1)訪問初始結(jié)點雅镊,并標(biāo)記結(jié)點v為已訪問。
(2)查找結(jié)點v的第一個鄰接結(jié)點w仁烹。
(3)若w存在耸弄,則繼續(xù)執(zhí)行4,否則算法結(jié)束卓缰。
(4)若w未被訪問计呈,對w進(jìn)行深度優(yōu)先遞歸(即把w當(dāng)做另一個v征唬,執(zhí)行算法1,2总寒,3)。
(5)查找v的w鄰接結(jié)點的下一個鄰接結(jié)點偿乖,轉(zhuǎn)到步驟3。
例如下圖贪薪,深度優(yōu)先遍歷的順序是1->2->4->8->5->3->6->7

深度優(yōu)先算法代碼和廣度合在一起了,都在下面

3.廣度優(yōu)先遍歷

廣度優(yōu)先遍歷類似與一個分層搜索的過程竣稽,廣度優(yōu)先遍歷需要使用一個隊列以保持訪問過的結(jié)點的順序霍弹,以便按照這個順序來訪問這個結(jié)點的鄰接結(jié)點毫别。算法的表述如下:

(1)訪問初始結(jié)點v并標(biāo)記結(jié)點v為已訪問典格。
(2)結(jié)點v入隊列。
(3)當(dāng)隊列非空時耍缴,繼續(xù)執(zhí)行挽霉,否則算法結(jié)束变汪。
(4)出隊列,取得隊頭結(jié)點u裙盾。
(5)查找結(jié)點u的第一個鄰接結(jié)點w。
(6)若結(jié)點u的鄰接結(jié)點w不存在番官,則轉(zhuǎn)到步驟3鲤拿;否則執(zhí)行下面3個步驟:
1)若結(jié)點w未被訪問署咽,則訪問結(jié)點w并標(biāo)記為已訪問。
2)結(jié)點w入隊列宁否。
3)查找結(jié)點u的繼w鄰接結(jié)點后的下一個鄰接結(jié)點,轉(zhuǎn)到步驟6慕匠。
例如下圖,廣度優(yōu)先遍歷的順序為:1->2->3->4->5->6->7->8


深度蓉媳,廣度優(yōu)先算法代碼锅铅,這個代碼用的鄰接表

package com.xushu.Undirectedgraph;

/**
 * Java: 鄰接表表示的"無向圖(List Undirected Graph)"
 */

import java.io.IOException;
import java.util.Scanner;

public class ListUDG {
    // 鄰接表中表對應(yīng)的鏈表的頂點
    private class ENode {
        int ivex;       // 該邊所指向的頂點的位置
        ENode nextEdge; // 指向下一條弧的指針
    }

    // 鄰接表中表的頂點
    private class VNode {
        char data;          // 頂點信息
        ENode firstEdge;    // 指向第一條依附該頂點的弧
    };

    private VNode[] mVexs;  // 頂點數(shù)組


//    /* 
//     * 創(chuàng)建圖(自己輸入數(shù)據(jù))
//     */
//    public ListUDG() {
//
//        // 輸入"頂點數(shù)"和"邊數(shù)"
//        System.out.printf("input vertex number: ");
//        int vlen = readInt();
//        System.out.printf("input edge number: ");
//        int elen = readInt();
//        if ( vlen < 1 || elen < 1 || (elen > (vlen*(vlen - 1)))) {
//            System.out.printf("input error: invalid parameters!\n");
//            return ;
//        }
//        
//        // 初始化"頂點"
//        mVexs = new VNode[vlen];
//        for (int i = 0; i < mVexs.length; i++) {
//            System.out.printf("vertex(%d): ", i);
//            mVexs[i] = new VNode();
//            mVexs[i].data = readChar();
//            mVexs[i].firstEdge = null;
//        }
//
//        // 初始化"邊"
//        //mMatrix = new int[vlen][vlen];
//        for (int i = 0; i < elen; i++) {
//            // 讀取邊的起始頂點和結(jié)束頂點
//            System.out.printf("edge(%d):", i);
//            char c1 = readChar();
//            char c2 = readChar();
//            int p1 = getPosition(c1);
//            int p2 = getPosition(c2);
//            // 初始化node1
//            ENode node1 = new ENode();
//            node1.ivex = p2;
//            // 將node1鏈接到"p1所在鏈表的末尾"
//            if(mVexs[p1].firstEdge == null)
//              mVexs[p1].firstEdge = node1;
//            else
//                linkLast(mVexs[p1].firstEdge, node1);
//            // 初始化node2
//            ENode node2 = new ENode();
//            node2.ivex = p1;
//            // 將node2鏈接到"p2所在鏈表的末尾"
//            if(mVexs[p2].firstEdge == null)
//              mVexs[p2].firstEdge = node2;
//            else
//                linkLast(mVexs[p2].firstEdge, node2);
//        }
//    }

    /*
     * 創(chuàng)建圖(用已提供的矩陣)
     *
     * 參數(shù)說明:
     *     vexs  -- 頂點數(shù)組
     *     edges -- 邊數(shù)組
     */
    public ListUDG(char[] vexs, char[][] edges) {
        
        // 初始化"頂點數(shù)"和"邊數(shù)"
        int vlen = vexs.length;
        int elen = edges.length;

        // 初始化"頂點"
        mVexs = new VNode[vlen];
        for (int i = 0; i < mVexs.length; i++) {
            mVexs[i] = new VNode();
            mVexs[i].data = vexs[i];
            mVexs[i].firstEdge = null;
        }

        // 初始化"邊"
        for (int i = 0; i < elen; i++) {
            // 讀取邊的起始頂點和結(jié)束頂點
            char c1 = edges[i][0];
            char c2 = edges[i][1];
            // 讀取邊的起始頂點和結(jié)束頂點
            int p1 = getPosition(edges[i][0]);
            int p2 = getPosition(edges[i][1]);

            // 初始化node1
            ENode node1 = new ENode();
            node1.ivex = p2;
            // 將node1鏈接到"p1所在鏈表的末尾"
            if(mVexs[p1].firstEdge == null)
              mVexs[p1].firstEdge = node1;
            else
                linkLast(mVexs[p1].firstEdge, node1);
            // 初始化node2
            ENode node2 = new ENode();
            node2.ivex = p1;
            // 將node2鏈接到"p2所在鏈表的末尾"
            if(mVexs[p2].firstEdge == null)
              mVexs[p2].firstEdge = node2;
            else
                linkLast(mVexs[p2].firstEdge, node2);
        }
    }

    /*
     * 將node節(jié)點鏈接到list的最后
     */
    private void linkLast(ENode list, ENode node) {
        ENode p = list;

        while(p.nextEdge!=null)
            p = p.nextEdge;
        p.nextEdge = node;
    }

    /*
     * 返回ch位置
     */
    private int getPosition(char ch) {
        for(int i=0; i<mVexs.length; i++)
            if(mVexs[i].data==ch)
                return i;
        return -1;
    }

    /*
     * 讀取一個輸入字符
     */
    private char readChar() {
        char ch='0';

        do {
            try {
                ch = (char)System.in.read();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } while(!((ch>='a'&&ch<='z') || (ch>='A'&&ch<='Z')));

        return ch;
    }

    /*
     * 讀取一個輸入字符
     */
    private int readInt() {
        Scanner scanner = new Scanner(System.in);
        return scanner.nextInt();
    }

    /*
     * 深度優(yōu)先搜索遍歷圖的遞歸實現(xiàn)
     */
    private void DFS(int i, boolean[] visited) {
        
        visited[i] = true;
        System.out.printf("%c ", mVexs[i].data);
        ENode node = mVexs[i].firstEdge;
        while (node != null) {
            if (!visited[node.ivex])
                DFS(node.ivex, visited);
            node = node.nextEdge;
        }
    }

    /*
     * 深度優(yōu)先搜索遍歷圖
     */
    public void DFSTravel() {
        boolean[] visited = new boolean[mVexs.length];       // 頂點訪問標(biāo)記

        // 初始化所有頂點都沒有被訪問
        for (int i = 0; i < mVexs.length; i++)
            visited[i] = false;

        System.out.printf("DFS: ");
        for (int i = 0; i < mVexs.length; i++) {
            if (!visited[i])
                DFS(i, visited);
        }
        System.out.printf("\n");
    }

    /*
     * 廣度優(yōu)先搜索(類似于樹的層次遍歷)
     */
    public void BFSTravel() {
        int head = 0;
        int rear = 0;
        int[] queue = new int[mVexs.length];            // 輔組隊列
        boolean[] visited = new boolean[mVexs.length];  // 頂點訪問標(biāo)記

        for (int i = 0; i < mVexs.length; i++)
            visited[i] = false;

        System.out.printf("BFS: ");
        for (int i = 0; i < mVexs.length; i++) {
            if (!visited[i]) {
                visited[i] = true;
                System.out.printf("%c ", mVexs[i].data);
                queue[rear++] = i;  // 入隊列
            }

            while (head != rear) { //當(dāng)隊列不空
                int j = queue[head++];  // 出隊列
                ENode node = mVexs[j].firstEdge;
                while (node != null) {
                    int k = node.ivex;
                    if (!visited[k])
                    {
                        visited[k] = true;
                        System.out.printf("%c ", mVexs[k].data);
                        queue[rear++] = k;
                    }
                    node = node.nextEdge;
                }
            }
        }
        System.out.printf("\n");
    }

    /*
     * 打印矩陣隊列圖
     */
    public void print() {
        System.out.printf("List Graph:\n");
        for (int i = 0; i < mVexs.length; i++) {
            System.out.printf("%d(%c): ", i, mVexs[i].data);
            ENode node = mVexs[i].firstEdge;
            while (node != null) {
                System.out.printf("%d(%c) ", node.ivex, mVexs[node.ivex].data);
                node = node.nextEdge;
            }
            System.out.printf("\n");
        }
    }

    public static void main(String[] args) {
        char[] vexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        char[][] edges = new char[][]{
            {'A', 'C'}, 
            {'A', 'D'}, 
            {'A', 'F'}, 
            {'B', 'C'}, 
            {'C', 'D'}, 
            {'E', 'G'}, 
            {'F', 'G'}};
        ListUDG pG;

        // 自定義"圖"(輸入矩陣隊列)
        //pG = new ListUDG();
        // 采用已有的"圖"
        pG = new ListUDG(vexs, edges);

        pG.print();   // 打印圖
        pG.DFSTravel();     // 深度優(yōu)先遍歷
        pG.BFSTravel();     // 廣度優(yōu)先遍歷
    }
}

二、拓?fù)渑判?/h1>

參考文獻(xiàn)

https://www.cnblogs.com/skywang12345/category/508186.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者

  • 序言:七十年代末贼邓,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子塑径,更是在濱河造成了極大的恐慌,老刑警劉巖堂飞,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異绰筛,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)铝噩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來骏庸,“玉大人,你說我怎么就攤上這事具被。” “怎么了一姿?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長艾栋。 經(jīng)常有香客問我,道長蛉顽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任悼粮,我火速辦了婚禮曾棕,結(jié)果婚禮上矮锈,老公的妹妹穿的比我還像新娘睁蕾。我一直安慰自己,他們只是感情好子眶,可當(dāng)我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著臭杰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪渴杆。 梳的紋絲不亂的頭發(fā)上宪塔,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天囊拜,我揣著相機(jī)與錄音,去河邊找鬼冠跷。 笑死南誊,一個胖子當(dāng)著我的面吹牛蜜托,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播橄务,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼重挑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蟆湖,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后隅津,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡伦仍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了充蓝。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡谓苟,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出涝焙,到底是詐尸還是另有隱情,我是刑警寧澤仑撞,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布妖滔,位于F島的核電站桶良,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏艺普。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一歧譬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧瑰步,春花似錦、人聲如沸缩焦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至揩徊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間塑荒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工齿税, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留炊豪,地道東北人凌箕。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓词渤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親掖肋。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,092評論 2 355