圖的深度優(yōu)先遍歷和廣度優(yōu)先遍歷

圖的遍歷

  • 圖的遍歷是和樹的遍歷類似,我們希望從圖中某一頂點出發(fā)訪遍圖中其余頂點贸街,且使每一個頂點僅被訪問一次沼琉,這一過程就叫做圖的遍歷(Traversing Graph)。
  • 重復訪問頂點就不叫做遍歷了奏夫。
  • 關(guān)于圖的基本概念怕篷,理論知識不想說了。太繁瑣~
  • 直接上圖酗昼,這個應該都能看懂廊谓。
圖的深度優(yōu)先遍歷.png
  • 左圖是一個,右圖是根據(jù)圖生成的矩陣麻削。
[v0][v1]代表v0頂點到v1頂點的路徑權(quán)重為10
可以看見右圖蒸痹,權(quán)重為0的都是頂點自己到自己,這根本就沒有意義呛哟。
而無限大符號表示到達不了叠荠,比如[v0][v2]∩ㄔ穑可以看左圖榛鼎,v0只能到達v1和v5,到不了v2鳖孤。

第一幅圖的矩陣可以看到有9個頂點者娱,組成二維數(shù)組

我們可以先生成這個矩陣:

public class Graph {
    
    private int vertexSize; // 頂點數(shù)量
    private int[] vertexs; // 頂點數(shù)組
    private int[][] matrix; // 包含所有頂點的數(shù)組
    // 路徑權(quán)重
    // 0意味著頂點自己到自己苏揣,無意義
    // MAX_WEIGHT也意味著到目的頂點不可達
    private static final int MAX_WEIGHT = 1000;
    
    public Graph(int vertextSize) {
        this.vertexSize = vertextSize;
        matrix = new int[vertextSize][vertextSize];
        vertexs = new int[vertextSize];
        for (int i = 0; i < vertextSize; i++) {
            vertexs[i] = i;
        }
    }
    
    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, 24, 16, 21 };
        //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, 24, MAX_WEIGHT, 17, 0, 19, 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;
    }
}
  • 可以看到我們用一個matrix二維數(shù)組存放所有頂點
  • 0表示自己到自己黄鳍,MAX_WEIGHT表示無限大
  • 然后就是一些實例化設置矩陣
  • 有了這些,我們才能進行遍歷

深度優(yōu)先遍歷

  • 深度優(yōu)先遍歷(Depth First Search)腿准,也有稱為深度優(yōu)先搜索际起,簡稱為DFS。
深度優(yōu)先遍歷.png
  • 遍歷規(guī)則:不斷的沿著頂點的深度方向遍歷吐葱。頂點的深度方向是指它的鄰接點方向街望。
  • 它從圖中某個頂點v出發(fā),訪問此頂點弟跑,然后從頂點的未被訪問的鄰接點出發(fā)深度優(yōu)先遍歷圖灾前,直至圖中所有和v有路徑相通的頂點都被訪問到。
  • 簡單說孟辑,就是頂點將第一個鄰接點當作左孩子哎甲,其它鄰接點都當做右孩子蔫敲。最后排成一棵樹。
  • 深度優(yōu)先遍歷是指先遍歷到最深層次炭玫,然后再探索鄰接點奈嘿,接著又遍歷最深層次。二叉樹的先序遍歷就是一種深度優(yōu)先遍歷吞加。

======================================

  • 思想與步驟
  • 看上圖右圖裙犹,我們先訪問A,然后訪問A的第一個鄰接點B衔憨。接著訪問B的第一個鄰接點C叶圃。。践图。掺冠。最后訪問到F,F(xiàn)想訪問第一個鄰接點A码党。但是A已經(jīng)訪問過了德崭,只能訪問F的下一個鄰接點G。
  • 這就是深度優(yōu)先遍歷的訪問順序闽瓢。
  • 在代碼中接癌,依照分析。獲取某頂點的第一個鄰接點和下一個臨界點是經(jīng)常使用的方法扣讼。訪問過程中缺猛,我們要判斷該頂點是否已訪問過,這個也需要輔助變量椭符。
private boolean[] isVisited = new boolean[vertextSize];

/**
 * 獲取指定頂點的第一個鄰接點
 * 
 * @param index
 *          指定鄰接點
 * @return
 */
private int getFirstNeighbor(int index) {
    for (int i = 0; i < vertexSize; i++) {
        if (matrix[index][i] < MAX_WEIGHT && matrix[index][i] > 0) {
            return i;
        }
    }
    return -1;
}

/**
 * 獲取指定頂點的下一個鄰接點
 * 
 * @param v
 *          指定的頂點
 * @param index
 *          從哪個鄰接點開始
 * @return
 */
private int getNextNeighbor(int v, int index) {
    for (int i = index+1; i < vertexSize; i++) {
        if (matrix[v][i] < MAX_WEIGHT && matrix[v][i] > 0) {
            return i;
        }
    }
    return -1;
}

核心代碼很簡單荔燎,經(jīng)上述分析過后:

/**
 * 圖的深度優(yōu)先遍歷算法
 */
private void depthFirstSearch(int i) {
    isVisited[i] = true;
    int w = getFirstNeighbor(i);
    while (w != -1) {
        if (!isVisited[w]) {
            // 需要遍歷該頂點
            System.out.println("訪問到了:" + w + "頂點");
            depthFirstSearch(w); // 進行深度遍歷
        }
        w = getNextNeighbor(i, w); // 第一個相對于w的鄰接點
    }
}
  • 0進去再层,表示v0绍哎。
  • 設置v0已訪問過,獲取v0第一個鄰接點械念。w != -1說明有這個鄰接點蒸健,然后對這個臨界點進行判斷座享。
    • 已訪問,那就找下一個臨界點
    • 未訪問似忧,進行訪問渣叛,然后對該鄰接點進行深度優(yōu)先遍歷
  • 算法還是很簡單的盯捌!

廣度優(yōu)先遍歷

  • 思想(感悟):
  • 廣度優(yōu)先遍歷表示把每一層都遍歷完才能遍歷下一層淳衙。
  • 我們來思考:假設v0有3個鄰接點,v1 v2 v3
    • 我們訪問v0后箫攀,然后訪問v1 v2 v3肠牲。完畢后我們要從v1開始遍歷它的鄰接點,接著從v2開始遍歷它的鄰接點靴跛,最后是從v3開始遍歷它的鄰接點缀雳。
    • 也就是說,3個鄰接點訪問完后汤求。我們要回過頭逐個遍歷它們的鄰接點俏险。這一點我覺得要用個容器把它們順序存儲下來。然后每次從容器首部取出一個頂點開始遍歷扬绪。這里我想到LinkedList,因為它適合增刪裤唠。而且這里不需要遍歷集合挤牛。
  • 整體步驟:
    • 我們可以把第一個頂點放進集合,然后while(!queue.isEmpty())while(queue.size() > 0)都行种蘸。開始循環(huán)墓赴。
    • 然后取出并刪除集合中第一個頂點元素的第一個鄰接點。對這個頂點進行訪問航瞭,
      • 如果該頂點未訪問過诫硕,就訪問!然后將該頂點放入集合刊侯。
      • 如果該頂點已訪問過章办,就找該頂點的下一個鄰接點。

核心代碼:

/**
 * 圖的廣度優(yōu)先遍歷算法
 */
private void boardFirstSearch(int i) {
    LinkedList<Integer> queue = new LinkedList<>(); 
    System.out.println("訪問到了:" + i + "頂點");
    isVisited[i] = true;
    queue.add(i);
    
    while (queue.size() > 0) {
        int w = queue.removeFirst().intValue();
        int n = getFirstNeighbor(w);
        while (n != -1) {
            if (!isVisited[n]) {
                System.out.println("訪問到了:" + n + "頂點");
                isVisited[n] = true;
                queue.add(n);
            }
            n = getNextNeighbor(w, n);
        }
    }
}

完整代碼

復制即可運行

import java.util.LinkedList;

public class Graph {
    
    private int vertexSize; // 頂點數(shù)量
    private int[] vertexs; // 頂點數(shù)組
    private int[][] matrix; // 包含所有頂點的數(shù)組
    // 路徑權(quán)重
    // 0意味著頂點自己到自己滨彻,無意義
    // MAX_WEIGHT也意味著到目的頂點不可達
    private static final int MAX_WEIGHT = 1000;
    private boolean[] isVisited; // 某頂點是否被訪問過
    
    public Graph(int vertextSize) {
        this.vertexSize = vertextSize;
        matrix = new int[vertextSize][vertextSize];
        vertexs = new int[vertextSize];
        for (int i = 0; i < vertextSize; i++) {
            vertexs[i] = i;
        }
        isVisited = new boolean[vertextSize];
    }
    
    /**
     * 獲取指定頂點的第一個鄰接點
     * 
     * @param index
     *          指定鄰接點
     * @return
     */
    private int getFirstNeighbor(int index) {
        for (int i = 0; i < vertexSize; i++) {
            if (matrix[index][i] < MAX_WEIGHT && matrix[index][i] > 0) {
                return i;
            }
        }
        return -1;
    }
    
    /**
     * 獲取指定頂點的下一個鄰接點
     * 
     * @param v
     *          指定的頂點
     * @param index
     *          從哪個鄰接點開始
     * @return
     */
    private int getNextNeighbor(int v, int index) {
        for (int i = index+1; i < vertexSize; i++) {
            if (matrix[v][i] < MAX_WEIGHT && matrix[v][i] > 0) {
                return i;
            }
        }
        return -1;
    }
    
    /**
     * 圖的深度優(yōu)先遍歷算法
     */
    private void depthFirstSearch(int i) {
        isVisited[i] = true;
        int w = getFirstNeighbor(i);
        while (w != -1) {
            if (!isVisited[w]) {
                // 需要遍歷該頂點
                System.out.println("訪問到了:" + w + "頂點");
                depthFirstSearch(w); // 進行深度遍歷
            }
            w = getNextNeighbor(i, w); // 第一個相對于w的鄰接點
        }
    }
    
    /**
     * 圖的廣度優(yōu)先遍歷算法
     */
    private void boardFirstSearch(int i) {
        LinkedList<Integer> queue = new LinkedList<>(); 
        System.out.println("訪問到了:" + i + "頂點");
        isVisited[i] = true;
        queue.add(i);
        
        while (queue.size() > 0) {
            int w = queue.removeFirst().intValue();
            int n = getFirstNeighbor(w);
            while (n != -1) {
                if (!isVisited[n]) {
                    System.out.println("訪問到了:" + n + "頂點");
                    isVisited[n] = true;
                    queue.add(n);
                }
                n = getNextNeighbor(w, n);
            }
        }
    }

    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, 24, 16, 21 };
        //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, 24, MAX_WEIGHT, 17, 0, 19, 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.depthFirstSearch(0);
        //graph.boardFirstSearch(0);
    }

}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末藕届,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子亭饵,更是在濱河造成了極大的恐慌休偶,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辜羊,死亡現(xiàn)場離奇詭異踏兜,居然都是意外死亡,警方通過查閱死者的電腦和手機八秃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進店門碱妆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人喜德,你說我怎么就攤上這事山橄。” “怎么了?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵航棱,是天一觀的道長睡雇。 經(jīng)常有香客問我,道長饮醇,這世上最難降的妖魔是什么它抱? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮朴艰,結(jié)果婚禮上观蓄,老公的妹妹穿的比我還像新娘。我一直安慰自己祠墅,他們只是感情好侮穿,可當我...
    茶點故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著毁嗦,像睡著了一般亲茅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上狗准,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天克锣,我揣著相機與錄音,去河邊找鬼腔长。 笑死袭祟,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的捞附。 我是一名探鬼主播巾乳,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼故俐!你這毒婦竟也來了想鹰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤药版,失蹤者是張志新(化名)和其女友劉穎辑舷,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體槽片,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡何缓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了还栓。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碌廓。...
    茶點故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖剩盒,靈堂內(nèi)的尸體忽然破棺而出谷婆,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布纪挎,位于F島的核電站期贫,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏异袄。R本人自食惡果不足惜通砍,卻給世界環(huán)境...
    茶點故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望烤蜕。 院中可真熱鬧封孙,春花似錦、人聲如沸讽营。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽橱鹏。三九已至呐籽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蚀瘸,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工庶橱, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留贮勃,地道東北人。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓苏章,卻偏偏與公主長得像寂嘉,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子枫绅,可洞房花燭夜當晚...
    茶點故事閱讀 45,585評論 2 359

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