Carson带你学数据结构:手把手带你了解 ”图“ 所有知识!(含DFS、BFS)
前言
本文主要講解 數(shù)據(jù)結(jié)構(gòu)中的圖 結(jié)構(gòu),包括 深度優(yōu)先搜索(DFS)、廣度優(yōu)先搜索(BFS)、最小生成樹算法等,希望你們會喜歡。
目錄
1. 簡介
- 數(shù)據(jù)結(jié)構(gòu)的中屬于 圓形結(jié)構(gòu) 的邏輯結(jié)構(gòu)
- 具體介紹如下
2. 基礎(chǔ)概念
- 在圖的數(shù)據(jù)結(jié)構(gòu)中,有許多基礎(chǔ)概念,如 邊類型、圖頂點 & 邊間的關(guān)系等等
- 具體請看下圖
3. 類型
圖的類型分為很多種,具體如下:
3.1 有向圖 & 無向圖
3.2 連通圖
-
定義
圖中任意頂點都是連通的圖 -
具體相關(guān)概念
對于有向圖 & 無向圖,連通圖的的具體概念又不同,具體如下
對于無向圖:
對于有向圖:
3.3 其余類型圖
4. 存儲結(jié)構(gòu)
圖的存儲結(jié)構(gòu)共有5種,具體請看下圖
5. 圖的遍歷
數(shù)據(jù)結(jié)構(gòu):圖文詳解二叉樹(遍歷、類型、操作)
5.1 定義
從圖中某1頂點出發(fā),遍歷圖中其余所有頂點 & 使每1個頂點僅被訪問1次
5.2 遍歷方式
深度優(yōu)先遍歷(DFS)、廣度優(yōu)先遍歷(BFS)
5.3 具體介紹
5.3.1 深度優(yōu)先遍歷( DFS )
- 簡介
- 算法示意圖
- 具體實現(xiàn):遞歸 & 非遞歸
此處圖的存儲結(jié)構(gòu) = 鄰接矩陣
import java.util.Stack;public class MyGraph {/*** 準(zhǔn)備工作1:設(shè)置變量*/private int vexnum; // 存放圖中頂點數(shù)量private char[] vertices; // 存放結(jié)點數(shù)據(jù)private int [][] arcs; // 存放圖的所有邊private boolean[] visited;// 記錄節(jié)點是否已被遍歷/*** 準(zhǔn)備工作2:初始化圖的頂點數(shù)量、數(shù)據(jù) & 邊*/public MyGraph(int n){vexnum = n;vertices = new char[n];visited = new boolean[n];arcs = new int[n][n];}/*** 圖的深度優(yōu)先遍歷算法實現(xiàn):遞歸* 類似 前序遍歷*/public void DFSTraverse(){// 1. 初始化所有頂點的訪問標(biāo)記// 即,都是未訪問狀態(tài)for (int i = 0; i < vexnum; i++) {visited[i] = false;}// 2. 深度優(yōu)先遍歷頂點(從未被訪問的頂點開始)for(int i=0;i < vexnum;i++){if(visited[i]==false){// 若是連通圖,只會執(zhí)行一次traverse(i); // ->>看關(guān)注1}}}/*** 關(guān)注1:鄰接矩陣的深度優(yōu)先搜索遞歸算法* 即,從第i個頂點開始深度優(yōu)先遍歷*/private void traverse(int i){// 1. 標(biāo)記第i個頂點已遍歷visited[i] = true;// 2. (輸出)訪問當(dāng)前遍歷的頂點visit(i);// 3. 遍歷鄰接矩陣中第i個頂點的所有鄰接頂點for(int j=0;j<vexnum;j++){// a. 若當(dāng)前頂點的鄰接頂點存在 & 未被訪問,則遞歸 深度優(yōu)先搜索 算法if(arcs[i][j]==1 && visited[j]==false){// b. 將當(dāng)前頂點的鄰接頂點作為當(dāng)前頂點,遞歸 深度優(yōu)先搜索 算法traverse(j);}}}/*** 輔助算法1:訪問頂點值*/public void visit(int i){System.out.print(vertices[i] + " ");}/*** 圖的深度優(yōu)先遍歷算法實現(xiàn):非遞歸* 原理:采用 棧實現(xiàn)*/public void DFSTraverse2(){// 1. 初始化頂點訪問標(biāo)記// 全部標(biāo)記為:未訪問for (int i = 0; i < vexnum; i++) {visited[i] = false;}// 2. 創(chuàng)建棧Stack<Integer> s = new Stack<Integer>();for(int i=0 ; i<vexnum; i++){// 3. 若該頂點未被訪問if(!visited[i]){// 4. 入棧該頂點s.add(i);do{// 出棧int curr = s.pop();// 如果該節(jié)點還沒有被遍歷,則遍歷該節(jié)點并將子節(jié)點入棧if(visited[curr]==false){// 遍歷并打印visit(curr);visited[curr] = true;// 沒遍歷的子節(jié)點入棧for(int j=vexnum-1; j>=0 ; j-- ){if(arcs[curr][j]==1 && visited[j]==false){s.add(j);}}}}while(!s.isEmpty());}}}/*** 測試(遞歸 & 非遞歸)*/public static void main(String[] args) {// 1. 初始化圖的結(jié)構(gòu)(頂點數(shù)量 = 9)MyGraph g = new MyGraph(9);// 2. 設(shè)置頂點數(shù)據(jù)char[] vertices = {'A','B','C','D','E','F','G','H','I'};g.setVertices(vertices);// 3. 設(shè)置邊g.addEdge(0, 1);g.addEdge(0, 5);g.addEdge(1, 0);g.addEdge(1, 2);g.addEdge(1, 6);g.addEdge(1, 8);g.addEdge(2, 1);g.addEdge(2, 3);g.addEdge(2, 8);g.addEdge(3, 2);g.addEdge(3, 4);g.addEdge(3, 6);g.addEdge(3, 7);g.addEdge(3, 8);g.addEdge(4, 3);g.addEdge(4, 5);g.addEdge(4, 7);g.addEdge(5, 0);g.addEdge(5, 4);g.addEdge(5, 6);g.addEdge(6, 1);g.addEdge(6, 3);g.addEdge(6, 5);g.addEdge(6, 7);g.addEdge(7, 3);g.addEdge(7, 4);g.addEdge(7, 6);g.addEdge(8, 1);g.addEdge(8, 2);g.addEdge(8, 3);// 4. 執(zhí)行 圖的深度優(yōu)先遍歷:(遞歸 & 非遞歸)System.out.println("深度優(yōu)先遍歷(遞歸)");g.DFSTraverse();System.out.println("深度優(yōu)先遍歷(非遞歸)");g.DFSTraverse2();}/*** 輔助算法1:添加邊(無向圖)*/public void addEdge(int i, int j) {// 邊的頭尾不能為同一節(jié)點if (i == j) return;// 將鄰接矩陣的第i行第j列的元素值置為1;arcs[i][j] = 1;// 將鄰接矩陣的第j行第i列的元素值置為1;arcs[j][i] = 1;// 設(shè)置為1代表2頂點之間存在 邊 (設(shè)置相等原因 = 鄰接矩陣 是對稱的)}/*** 輔助算法2:設(shè)置頂點數(shù)據(jù)*/public void setVertices(char[] vertices) {this.vertices = vertices;} }- 測試結(jié)果
-
特別注意
對于圖的存儲結(jié)構(gòu) = 鄰接表實現(xiàn),只需要將 存儲邊 的2維數(shù)組 改成鏈表即可。 -
圖的存儲結(jié)構(gòu) = 鄰接矩陣 / 鄰接表 的性能對比
5.3.2 廣度優(yōu)先遍歷(BFS)
- 簡介
- 算法示意圖
- 具體流程
注:G 比 I 先訪問的原因 = 用數(shù)組存儲頂點時,G的下標(biāo) 比 I的下標(biāo)小(按ABCDEFGHI順序存儲)
- 具體實現(xiàn)
非遞歸:采用隊列
import java.util.LinkedList; import java.util.Queue; import java.util.Stack;public class MyGraph {/*** 準(zhǔn)備工作1:設(shè)置變量*/private int vexnum; // 存放圖中頂點數(shù)量private char[] vertices; // 存放結(jié)點數(shù)據(jù)private int [][] arcs; // 存放圖的所有邊private boolean[] visited;// 記錄節(jié)點是否已被遍歷/*** 準(zhǔn)備工作2:初始化圖的頂點數(shù)量、數(shù)據(jù) & 邊*/public MyGraph(int n){vexnum = n;vertices = new char[n];visited = new boolean[n];arcs = new int[n][n];}/*** 廣度優(yōu)先搜索 算法實現(xiàn)* 原理:非遞歸(采用隊列)*/public void BFS(){// 1. 初始化所有頂點的訪問標(biāo)記// 即,設(shè)置為未訪問狀態(tài)for (int i = 0; i < vexnum; i++) {visited[i] = false;}// 2. 創(chuàng)建隊列Queue<Integer> q=new LinkedList<Integer>();// 3. 對所有頂點做遍歷循環(huán)(從第1個頂點開始)// 若遍歷完畢,則結(jié)束整個層序遍歷for(int i=0;i < vexnum;i++){// 4. 若當(dāng)前頂點未被訪問,就進行處理// 若當(dāng)前頂點已被訪問,則回到3進行判斷if( visited[i]==false ) {// 5. (輸出)訪問當(dāng)前頂點visit(i);// 6. 標(biāo)記當(dāng)前頂點已被訪問visited[i] = true;// 7. 入隊當(dāng)前頂點q.add(i);// 8.判斷當(dāng)前隊列是否為空// 若為空則跳出循環(huán),回到3進行判斷while(!q.isEmpty()) {// 9. 出隊隊首元素 & 將出隊的元素 賦值為 當(dāng)前頂點i = q.poll();// 10. 遍歷當(dāng)前頂點的所有鄰接點// 若遍歷完畢,則回到8判斷for(int j=0; j< vexnum ; j++){// 11. 若當(dāng)前頂點的鄰接頂點存在 & 未被訪問,則執(zhí)行處理// 否則回到10判斷if(arcs[i][j]==1 && visited[j]==false){// 12. (輸出)訪問當(dāng)前頂點的鄰接頂點visit(j);// 13. 標(biāo)記當(dāng)前頂點的鄰接頂點已被訪問visited[j] = true;// 14. 入隊當(dāng)前頂點的鄰接頂點q.add(j);}}}}}}/*** 輔助算法1:訪問該頂點*/public void visit(int i){System.out.print(vertices[i] + " ");}/** * 測試算法*/public static void main(String[] args) {// 1. 初始化圖的結(jié)構(gòu)(頂點數(shù)量 = 9MyGraph g = new MyGraph(9);// 2. 設(shè)置頂點數(shù)據(jù)char[] vertices = {'A','B','C','D','E','F','G','H','I'};g.setVertices(vertices);// 3. 設(shè)置邊g.addEdge(0, 1);g.addEdge(0, 5);g.addEdge(1, 0);g.addEdge(1, 2);g.addEdge(1, 6);g.addEdge(1, 8);g.addEdge(2, 1);g.addEdge(2, 3);g.addEdge(2, 8);g.addEdge(3, 2);g.addEdge(3, 4);g.addEdge(3, 6);g.addEdge(3, 7);g.addEdge(3, 8);g.addEdge(4, 3);g.addEdge(4, 5);g.addEdge(4, 7);g.addEdge(5, 0);g.addEdge(5, 4);g.addEdge(5, 6);g.addEdge(6, 1);g.addEdge(6, 3);g.addEdge(6, 5);g.addEdge(6, 7);g.addEdge(7, 3);g.addEdge(7, 4);g.addEdge(7, 6);g.addEdge(8, 1);g.addEdge(8, 2);g.addEdge(8, 3);// 4. 執(zhí)行 圖的廣度優(yōu)先遍歷(非遞歸)System.out.print("廣度優(yōu)先遍歷(非遞歸):");g.BFS();}/*** 輔助算法1:添加邊(無向圖)*/public void addEdge(int i, int j) {// 邊的頭尾不能為同一節(jié)點if (i == j) return;// 將鄰接矩陣的第i行第j列的元素值置為1;arcs[i][j] = 1;// 將鄰接矩陣的第j行第i列的元素值置為1;arcs[j][i] = 1;// 設(shè)置為1代表2頂點之間存在 邊 (設(shè)置相等原因 = 鄰接矩陣 是對稱的)}/*** 輔助算法2:設(shè)置頂點數(shù)據(jù)*/public void setVertices(char[] vertices) {this.vertices = vertices;}}- 執(zhí)行結(jié)果
5.4 遍歷方式對比
6. 最小生成樹
本節(jié)主要講解 圖中的 最小生成樹
6.1 定義
構(gòu)造 連通網(wǎng)圖 的最小成本生成樹
6.2 尋找最小生成樹的算法
- 主要包括:(Prim)普利姆算法 & (Kruskal)克魯斯卡爾算法
- 具體介紹如下
6.2.1 (Prim)普利姆算法
- 算法概述
- 算法原理流程示意圖
- 舉例說明
6.2.2(Kruskal)克魯斯卡爾算法
- 算法概述
6.3 算法對比
7. 最短路徑
7.1 定義
- 對于非網(wǎng)圖(無權(quán)值),最短路徑 = 兩頂點間經(jīng)過的邊數(shù)最少的路徑
- 對于網(wǎng)圖(有權(quán)值):最短路徑 = 兩頂點間經(jīng)過的邊上權(quán)值和最少的路徑
第1個頂點 = 源點、第2個頂點 = 終點
7.2 需解決的問題
從源點 -> 其余各頂點的最短路徑
7.3 尋找最短路徑 算法
-
主要包括:迪杰斯特拉算法(Dijkstra)、弗洛伊德算法(Floyd)
-
具體介紹如下
8. 總結(jié)
- 本文主要講解了數(shù)據(jù)結(jié)構(gòu)中的圖
- 下面我將繼續(xù)對 數(shù)據(jù)結(jié)構(gòu),有興趣可以繼續(xù)關(guān)注Carson_Ho的安卓開發(fā)筆記
幫頂 / 評論點贊!因為你的鼓勵是我寫作的最大動力!
總結(jié)
以上是生活随笔為你收集整理的Carson带你学数据结构:手把手带你了解 ”图“ 所有知识!(含DFS、BFS)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 手游开发攻防——二、基础篇
- 下一篇: 国家级示范高等职业院校网址