數(shù)據(jù)結(jié)構(gòu)–圖(深度優(yōu)先遍歷和廣度優(yōu)先遍歷)(Java)

數(shù)據(jù)結(jié)構(gòu)–圖(深度優(yōu)先遍歷和廣度優(yōu)先遍歷)(Java)

博客說明

文章所涉及的資料來自互聯(lián)網(wǎng)整理和個(gè)人總結(jié)朽缴,意在于個(gè)人學(xué)習(xí)和經(jīng)驗(yàn)匯總五续,如有什么地方侵權(quán),請(qǐng)聯(lián)系本人刪除樊卓,謝謝!

圖的常用概念

圖是一種數(shù)據(jù)結(jié)構(gòu)杠河,其中結(jié)點(diǎn)可以具有零個(gè)或多個(gè)相鄰元素碌尔。兩個(gè)結(jié)點(diǎn)之間的連接稱為邊。 結(jié)點(diǎn)也可以稱為頂點(diǎn)券敌。

  • 頂點(diǎn)(vertex)
  • 邊(edge)
  • 路徑
  • 無向圖
image-20200904124205865
  • 有向圖
image-20200904124318805
  • 帶權(quán)圖
image-20200904124332489

圖的表示方式

圖的表示方式有兩種:二維數(shù)組表示(鄰接矩陣)唾戚;鏈表表示(鄰接表)。

鄰接矩陣

鄰接矩陣是表示圖形中頂點(diǎn)之間相鄰關(guān)系的矩陣待诅,對(duì)于n個(gè)頂點(diǎn)的圖而言叹坦,矩陣是的row和col表示的是1....n個(gè)點(diǎn)。

image-20200904124501028
鄰接表

鄰接矩陣需要為每個(gè)頂點(diǎn)都分配n個(gè)邊的空間卑雁,其實(shí)有很多邊都是不存在,會(huì)造成空間的一定損失

鄰接表的實(shí)現(xiàn)只關(guān)心存在的邊募书,不關(guān)心不存在的邊轧钓。因此沒有空間浪費(fèi),鄰接表由數(shù)組+鏈表組成

image-20200904124627030

代碼實(shí)現(xiàn)

package com.guizimo;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;

public class Graph {

    private ArrayList<String> vertexList; 
    private int[][] edges; 
    private int numOfEdges; 
    private boolean[] isVisited;
    
    public static void main(String[] args) {

        int n = 8;
        String Vertexs[] = {"1", "2", "3", "4", "5", "6", "7", "8"};
        Graph graph = new Graph(n);
        for(String vertex: Vertexs) {
            graph.insertVertex(vertex);
        }
        
        //插入圖的節(jié)點(diǎn)
        graph.insertEdge(0, 1, 1);
        graph.insertEdge(0, 2, 1);
        graph.insertEdge(1, 3, 1);
        graph.insertEdge(1, 4, 1);
        graph.insertEdge(3, 7, 1);
        graph.insertEdge(4, 7, 1);
        graph.insertEdge(2, 5, 1);
        graph.insertEdge(2, 6, 1);
        graph.insertEdge(5, 6, 1);

        //遍歷圖
        graph.showGraph();
        
        System.out.println("廣度優(yōu)先遍歷
        graph.dfs(); 
        System.out.println("深度優(yōu)先遍歷
        graph.bfs();
        
    }
    
    public Graph(int n) {
        edges = new int[n][n];
        vertexList = new ArrayList<String>(n);
        numOfEdges = 0;
    }
    

    public int getFirstNeighbor(int index) {
        for(int j = 0; j < vertexList.size(); j++) {
            if(edges[index][j] > 0) {
                return j;
            }
        }
        return -1;
    }

    public int getNextNeighbor(int v1, int v2) {
        for(int j = v2 + 1; j < vertexList.size(); j++) {
            if(edges[v1][j] > 0) {
                return j;
            }
        }
        return -1;
    }
    
    //深度優(yōu)先遍歷
    private void dfs(boolean[] isVisited, int i) {
        System.out.print(getValueByIndex(i) + "->");
        isVisited[i] = true;
        int w = getFirstNeighbor(i);
        while(w != -1) {
            if(!isVisited[w]) {
                dfs(isVisited, w);
            }
            w = getNextNeighbor(i, w);
        }
        
    }
    
    public void dfs() {
        isVisited = new boolean[vertexList.size()];
        for(int i = 0; i < getNumOfVertex(); i++) {
            if(!isVisited[i]) {
                dfs(isVisited, i);
            }
        }
    }
    
    //廣度優(yōu)先遍歷
    private void bfs(boolean[] isVisited, int i) {
        int u ; 
        int w ; 
        LinkedList queue = new LinkedList();
        System.out.print(getValueByIndex(i) + "=>");
        isVisited[i] = true;
        queue.addLast(i);
        
        while( !queue.isEmpty()) {
            u = (Integer)queue.removeFirst();
            w = getFirstNeighbor(u);
            while(w != -1) {
                if(!isVisited[w]) {
                    System.out.print(getValueByIndex(w) + "=>");
                    isVisited[w] = true;
                    queue.addLast(w);
                }
                w = getNextNeighbor(u, w); 
            }
        }
        
    } 
    
    public void bfs() {
        isVisited = new boolean[vertexList.size()];
        for(int i = 0; i < getNumOfVertex(); i++) {
            if(!isVisited[i]) {
                bfs(isVisited, i);
            }
        }
    }
    
    public int getNumOfVertex() {
        return vertexList.size();
    }
                       
    //遍歷
    public void showGraph() {
        for(int[] link : edges) {
            System.err.println(Arrays.toString(link));
        }
    }
                       
    public int getNumOfEdges() {
        return numOfEdges;
    }

    public String getValueByIndex(int i) {
        return vertexList.get(i);
    }
                       
    public int getWeight(int v1, int v2) {
        return edges[v1][v2];
    }
                       
    //添加鄰接矩陣
    public void insertVertex(String vertex) {
        vertexList.add(vertex);
    }
    
  //插入權(quán)值
    public void insertEdge(int v1, int v2, int weight) {
        edges[v1][v2] = weight;
        edges[v2][v1] = weight;
        numOfEdges++;
    }
}

圖的深度優(yōu)先搜索(Depth First Search)

深度優(yōu)先遍歷锐膜,從初始訪問結(jié)點(diǎn)出發(fā)毕箍,初始訪問結(jié)點(diǎn)可能有多個(gè)鄰接結(jié)點(diǎn),深度優(yōu)先遍歷的策略就是首先訪問第一個(gè)鄰接結(jié)點(diǎn)道盏,然后再以這個(gè)被訪問的鄰接結(jié)點(diǎn)作為初始結(jié)點(diǎn)而柑,訪問它的第一個(gè)鄰接結(jié)點(diǎn), 可以這樣理解:每次都在訪問完當(dāng)前結(jié)點(diǎn)后首先訪問當(dāng)前結(jié)點(diǎn)的第一個(gè)鄰接結(jié)點(diǎn)

算法
  • 訪問初始結(jié)點(diǎn)v荷逞,并標(biāo)記結(jié)點(diǎn)v為已訪問媒咳。
  • 查找結(jié)點(diǎn)v的第一個(gè)鄰接結(jié)點(diǎn)w。
  • 若w存在种远,則繼續(xù)執(zhí)行4涩澡,如果w不存在,則回到第1步坠敷,將從v的下一個(gè)結(jié)點(diǎn)繼續(xù)妙同。
  • 若w未被訪問,對(duì)w進(jìn)行深度優(yōu)先遍歷遞歸(即把w當(dāng)做另一個(gè)v膝迎,然后進(jìn)行步驟123)粥帚。
  • 查找結(jié)點(diǎn)v的w鄰接結(jié)點(diǎn)的下一個(gè)鄰接結(jié)點(diǎn),轉(zhuǎn)到步驟3
代碼
//深度優(yōu)先遍歷
private void dfs(boolean[] isVisited, int i) {
  System.out.print(getValueByIndex(i) + "->");
  isVisited[i] = true;
  int w = getFirstNeighbor(i);
  while(w != -1) {
    if(!isVisited[w]) {
      dfs(isVisited, w);
    }
    w = getNextNeighbor(i, w);
  }

}

public void dfs() {
  isVisited = new boolean[vertexList.size()];
  for(int i = 0; i < getNumOfVertex(); i++) {
    if(!isVisited[i]) {
      dfs(isVisited, i);
    }
  }
}

圖的廣度優(yōu)先搜索(Broad First Search)

類似于一個(gè)分層搜索的過程限次,廣度優(yōu)先遍歷需要使用一個(gè)隊(duì)列以保持訪問過的結(jié)點(diǎn)的順序芒涡,以便按這個(gè)順序來訪問這些結(jié)點(diǎn)的鄰接結(jié)點(diǎn)

算法
  • 訪問初始結(jié)點(diǎn)v并標(biāo)記結(jié)點(diǎn)v為已訪問。
  • 結(jié)點(diǎn)v入隊(duì)列
  • 當(dāng)隊(duì)列非空時(shí)卖漫,繼續(xù)執(zhí)行费尽,否則算法結(jié)束。
  • 出隊(duì)列羊始,取得隊(duì)頭結(jié)點(diǎn)u旱幼。
  • 查找結(jié)點(diǎn)u的第一個(gè)鄰接結(jié)點(diǎn)w。
  • 若結(jié)點(diǎn)u的鄰接結(jié)點(diǎn)w不存在店枣,則轉(zhuǎn)到步驟3速警;否則循環(huán)執(zhí)行以下三個(gè)步驟:
    • 若結(jié)點(diǎn)w尚未被訪問,則訪問結(jié)點(diǎn)w并標(biāo)記為已訪問鸯两。
    • 結(jié)點(diǎn)w入隊(duì)列
    • 查找結(jié)點(diǎn)u的繼w鄰接結(jié)點(diǎn)后的下一個(gè)鄰接結(jié)點(diǎn)w,轉(zhuǎn)到步驟6
代碼
//廣度優(yōu)先遍歷
private void bfs(boolean[] isVisited, int i) {
  int u ; 
  int w ; 
  LinkedList queue = new LinkedList();
  System.out.print(getValueByIndex(i) + "=>");
  isVisited[i] = true;
  queue.addLast(i);

  while( !queue.isEmpty()) {
    u = (Integer)queue.removeFirst();
    w = getFirstNeighbor(u);
    while(w != -1) {
      if(!isVisited[w]) {
        System.out.print(getValueByIndex(w) + "=>");
        isVisited[w] = true;
        queue.addLast(w);
      }
      w = getNextNeighbor(u, w); 
    }
  }

} 

public void bfs() {
  isVisited = new boolean[vertexList.size()];
  for(int i = 0; i < getNumOfVertex(); i++) {
    if(!isVisited[i]) {
      bfs(isVisited, i);
    }
  }
}

感謝

尚硅谷

以及勤勞的自己长豁,個(gè)人博客钧唐,GitHub

微信公眾號(hào)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市匠襟,隨后出現(xiàn)的幾起案子钝侠,更是在濱河造成了極大的恐慌该园,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件帅韧,死亡現(xiàn)場(chǎng)離奇詭異里初,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)忽舟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門双妨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人叮阅,你說我怎么就攤上這事刁品。” “怎么了浩姥?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵挑随,是天一觀的道長。 經(jīng)常有香客問我勒叠,道長兜挨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任眯分,我火速辦了婚禮暑劝,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘颗搂。我一直安慰自己担猛,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布丢氢。 她就那樣靜靜地躺著傅联,像睡著了一般。 火紅的嫁衣襯著肌膚如雪疚察。 梳的紋絲不亂的頭發(fā)上蒸走,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音貌嫡,去河邊找鬼比驻。 笑死,一個(gè)胖子當(dāng)著我的面吹牛岛抄,可吹牛的內(nèi)容都是我干的别惦。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼夫椭,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼掸掸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤扰付,失蹤者是張志新(化名)和其女友劉穎堤撵,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體羽莺,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡实昨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了盐固。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荒给。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖闰挡,靈堂內(nèi)的尸體忽然破棺而出锐墙,到底是詐尸還是另有隱情,我是刑警寧澤长酗,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布溪北,位于F島的核電站,受9級(jí)特大地震影響夺脾,放射性物質(zhì)發(fā)生泄漏之拨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一咧叭、第九天 我趴在偏房一處隱蔽的房頂上張望蚀乔。 院中可真熱鬧,春花似錦菲茬、人聲如沸吉挣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽睬魂。三九已至,卻和暖如春镀赌,著一層夾襖步出監(jiān)牢的瞬間氯哮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國打工商佛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留喉钢,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓良姆,卻偏偏與公主長得像肠虽,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子歇盼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345