日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

数据结构 - 图 (图的深度优先和广度优先)

發布時間:2023/12/31 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构 - 图 (图的深度优先和广度优先) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

圖的基本介紹

為什么要有圖這種數據結構

  • 數據結構有線性表和樹
  • 線性表局限與一個直接前驅和一個直接后繼的關系
  • 樹也只能右一個直接前驅也就是父節點
  • 當我們需要表示多對多的關系時,這里我們就需要用到圖這種數據結構
  • 圖是一種數據結構,其中節點可以具有零個或者多個相鄰的元素。2個節點之間的連接稱為邊。節點也可以稱為頂點。 如圖:

    圖的常用概念
    1. 頂點(vertex)
    2. 邊(edge)
    3. 路徑
    4. 無向圖(如圖)

    5. 有向圖 (如下圖)
    6. 帶權圖

    圖的表示方式

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

    鄰接矩陣

    鄰接矩陣是表示圖形中頂點之間相鄰關系的矩陣,對于n個頂點的圖而言,矩陣是的row和 col表示的是1…n個點。

    鄰接表

  • 鄰接矩陣需要為每個頂點都分配n個邊的空間,其實有很多邊都是不存在,會造成空間的一定損失.
  • 鄰接表的實現只關心存在的邊,不關心不存在的邊。因此沒有空間浪費,鄰接表由數組+鏈表組成
  • 圖的案例入門
    要求:代碼實現如下圖結構

    思路:
    保存頂點用ArrayList集合 ,保存頂點之間相鄰關系的用鄰接矩陣,即二維數組表示。

    public class Graph {// 存放頂點的集合private List<String> vertexList;// 鄰接矩陣,存放頂點關系private int[][] edges;// 邊的數目private int numOfEdges;public Graph(int n) {// n表示頂點的個數vertexList = new ArrayList<>(n);edges = new int[n][n];numOfEdges = 0;}/*** 添加頂點的方法* @param vertex*/public void addVertex(String vertex) {vertexList.add(vertex);}/*** 獲取頂點個數*/public int getVertexNum() {return vertexList.size();}/*** 返回對于下標的頂點* @param i* @return*/public String getVertex(int i) {return vertexList.get(i);}/*** 添加邊* @param v1 表示第幾個下標的頂點的位置* @param v2 表示第二個頂點的下標* @param weight 邊的權*/public void addEdge(int v1, int v2, int weight) {// 表示下標v1和v2頂點的邊edges[v1][v2] = weight;// 表示下標v2和v1頂點的邊edges[v2][v1] = weight;numOfEdges++;}/*** 遍歷頂點*/public void listVertex() {for (String s : vertexList) {System.out.print(s + " ");}}/*** 遍歷鄰接矩陣,邊*/public void listEdge() {for (int[] edge : edges) {System.out.println(Arrays.toString(edge));}} }

    測試

    public class MyTest {public static void main(String[] args) {Graph graph = new Graph(5);String[] vertexs = {"A", "B", "C", "D", "E"};for (String vertex : vertexs) {// 添加頂點graph.addVertex(vertex);}// 添加邊 A-Bgraph.addEdge(0,1,1);// 添加邊 A-Cgraph.addEdge(0,2,1);// 添加邊 B-Cgraph.addEdge(1,2,1);// 添加邊 B-Dgraph.addEdge(1,3,1);// 添加邊 B-Egraph.addEdge(1,4,1);// 遍歷頂點graph.listVertex();System.out.println();// 遍歷邊graph.listEdge();} }

    輸出結果

    A B C D E [0, 1, 1, 0, 0] [1, 0, 1, 1, 1] [1, 1, 0, 0, 0] [0, 1, 0, 0, 0] [0, 1, 0, 0, 0]

    圖的深度優先遍歷

    • 圖的遍歷介紹

      所謂圖的遍歷,即使對節點的訪問。一個圖右很多節點,如何遍歷這些節點,我們需要一定的策略,一般有兩種策略:1.深度優先遍歷,2.廣度優先遍歷

    • 圖的深度優先遍歷(Depth First Search)

      • 深度優先遍歷,從初始訪問結點出發,初始訪問結點可能有多個鄰接結點,深度優先遍歷的策略就是首先訪問第一個鄰接結點,然后再以這個被訪問的鄰接結點作為初始結點,訪問它的第一個鄰接結點, 可以這樣理解:每次都在訪問完當前結點后首先訪問當前結點的第一個鄰接結點。
      • 我們可以看到,這樣的訪問策略是優先往縱向挖掘深入,而不是對一個結點的所有鄰接結點進行橫向訪問。
      • 顯然,深度優先搜索是一個遞歸的過程
    • 深度優先遍歷算法步驟

    • 訪問初始節點V,并標記該節點V為已訪問。
    • 查找節點V的第一個鄰接節點W。
    • 若W存在,則執行步驟4,若W不存在,則回到步驟1,將從V的下一個連接節點繼續
    • 若W未被訪問,對W進行深度優先遍歷遞歸(即把 W當做另一個V,然后進行步驟123)。
    • 若W已被訪問,則查找V的W的鄰接節點的下一個鄰接節點,然后轉到步驟3。

    圖的廣度優先遍歷

    圖的廣度優先搜索類似于一個分成搜索過程,廣度優先遍歷需要使用一個隊列以保持訪問過節點的順序,以便按這個順序來訪問這些節點的鄰接節點

    • 廣度優先遍歷算法步驟
    • 訪問初始節點V,并標記節點V為已訪問。
    • 節點V入隊列
    • 當隊列非空時,繼續執行,否則算法結束
    • 出隊列,取得隊頭結點u。
    • 查找結點u的第一個鄰接結點w。
    • 若結點u的鄰接結點w不存在,則轉到步驟3;否則循環執行以下三個 步驟
      6.1 若結點w尚未被訪問,則訪問結點w并標記為已訪問。
      6.2 結點w入隊列
      6.3 查找結點u的繼w鄰接結點后的下一個鄰接結點w,轉到步驟6。

    深度優先遍歷和廣度優先遍歷代碼示例

    主要方法:深度優先(dfs),廣度優先(bfs)

    public class Graph {// 存放頂點的集合private List<String> vertexList;// 鄰接矩陣,存放頂點關系private int[][] edges;// 邊的數目private int numOfEdges;// 是否被訪問private boolean[] isVisited;public Graph(int n) {// n表示頂點的個數vertexList = new ArrayList<>(n);edges = new int[n][n];numOfEdges = 0;}/*** 添加頂點的方法* @param vertex*/public void addVertex(String vertex) {vertexList.add(vertex);}/*** 查找下標為index頂點的的第一個鄰接節點* @param index* @return 返回找到的下標 ,沒有找到就返回-1*/public int getFirstNeighbor(int index) {for (int i = 0; i < vertexList.size(); i++) {if (edges[index][i] == 1){return i;}}return -1;}/**** @param index 初始頂點下標* @param firstNeighbor 初始頂點的第一個個鄰接節點* @return 返回初始節點的下一個鄰接節點的下標,沒有就返回-1*/public int getNextNeighbor(int index, int firstNeighbor) {for (int i = firstNeighbor + 1; i < vertexList.size(); i++) {if (edges[index][i] == 1) {return i;}}return -1;}/*** 深度優先遍歷* @param isVisited 要訪問的節點是否被訪問* @param index 要訪問的初始節點*/private void dfs(boolean[] isVisited, int index) {isVisited = new boolean[vertexList.size()];// 1. 訪問初始節點,并把該節點標記為已訪問System.out.print(vertexList.get(index) + "->");isVisited[index] = true;//2. 查找節點index的第一個鄰接節點int firstNeighbor = getFirstNeighbor(index);//3. firstNeighbor != -1 說明有鄰接節點while ( firstNeighbor != -1) {// 判斷這鄰接節點是否被訪問if (!isVisited[firstNeighbor]) {//4. 沒有被訪問,則進行遞歸,把firstNeighbor當成初始節點進行遞歸訪問dfs(isVisited, firstNeighbor);}// 5.該鄰接節點已被訪問,則查找index除了firstNeighbor節點的下一個鄰接節點firstNeighbor = getNextNeighbor(index, firstNeighbor);}}/*** 深度優先遍歷*/public void dfs () {for (int i = 0; i < vertexList.size(); i++) {if (!isVisited[i]) {dfs(isVisited, i);}}}/*** 廣度優先遍歷* @param isVisited* @param index*/private void bfs(boolean[] isVisited, int index) {isVisited = new boolean[vertexList.size()];// 定義一個隊列,來保存訪問過節點的順序LinkedList<Integer> queue = new LinkedList<>();// 1. 訪問初始節點,標記為已訪問System.out.println(vertexList.get(index) + "->");isVisited[index] = true;// 將訪問過的節點入隊列,注意加 到最后一個,取的時候是第一個queue.addLast(index);// 當隊列不為空時,一直循環執行int v; // 用來存放隊列里去除的節點下標while (!queue.isEmpty()) {// 取出隊列的頭節點v = queue.removeFirst();// 查找節點v的鄰接節點int neighbor = getFirstNeighbor(v);// 若鄰接節點存在while (neighbor != -1) {// 若該節點未被訪問if (!isVisited[neighbor]) {System.out.print(vertexList.get(neighbor) + "->");// 標記為已訪問isVisited[neighbor] = true;// 加入隊列queue.addLast(neighbor);}// 查找v的繼neighbor的下一個鄰接節點neighbor = getNextNeighbor(v, neighbor);}}}public void bfs() {for (int i = 0; i < vertexList.size(); i++) {if (!isVisited[i]) {bfs(isVisited,i);}}}/*** 獲取頂點個數*/public int getVertexNum() {return vertexList.size();}/*** 返回對于下標的頂點* @param i* @return*/public String getVertex(int i) {return vertexList.get(i);}/*** 添加邊* @param v1 表示第幾個下標的頂點的位置* @param v2 表示第二個頂點的下標* @param weight 邊的權*/public void addEdge(int v1, int v2, int weight) {// 表示下標v1和v2頂點的邊edges[v1][v2] = weight;// 表示下標v2和v1頂點的邊edges[v2][v1] = weight;numOfEdges++;}/*** 遍歷頂點*/public void listVertex() {for (String s : vertexList) {System.out.print(s + " ");}}/*** 遍歷鄰接矩陣,邊*/public void listEdge() {for (int[] edge : edges) {System.out.println(Arrays.toString(edge));}} }

    測試類

    public class MyTest {public static void main(String[] args) {Graph graph = new Graph(5);String[] vertexs = {"A", "B", "C", "D", "E"};for (String vertex : vertexs) {// 添加頂點graph.addVertex(vertex);}// 添加邊 A-Bgraph.addEdge(0,1,1);// 添加邊 A-Cgraph.addEdge(0,2,1);// 添加邊 B-Cgraph.addEdge(1,2,1);// 添加邊 B-Dgraph.addEdge(1,3,1);// 添加邊 B-Egraph.addEdge(1,4,1);System.out.println("對圖進行深度優先遍歷:");graph.dfs();System.out.println();System.out.println("廣度優先遍歷");graph.bfs();} }

    測試結果

    對圖進行深度優先遍歷: A->B->C->D->E-> 廣度優先遍歷 A->B->C->D->E->

    總結:注意分析代碼,深度優先和廣度優先主要區別在于:

  • 深度優先是先從初始節點開始,尋找到他的下一個鄰節點,然后在以這個鄰接點為初始節點,繼續重復這些操作。
  • 廣度優先是從初始節點開始,尋找他的鄰節點,然后再繼續尋找他下一個鄰節點,直到把他所有的鄰節點找到,在換成隊列另一個節點繼續重復上面的操作,
  • 總結

    以上是生活随笔為你收集整理的数据结构 - 图 (图的深度优先和广度优先)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。