[JAVA]圖(Graph)的深度優先搜尋(DFS)vs.廣度優先搜尋(BFS)

 深度優先搜尋(DFS: Depth-First Search):
   * 深度優先遍歷,從初始訪問點出發,初始訪問點可能有多個鄰接結點,
   * 深度優先的遍歷的策略是首先訪問第一個鄰接點,
   * 然後再以這個被訪問的鄰接結點作為初始訪問結點,
   * 訪問它的第一個鄰接結點,
   * 即每一次訪問完當前節點都會首先訪問當前節點的第一個鄰節點。類似迷宮回溯算法。
   * 深度是盡可能找下一層的結點
 廣度優先搜尋(Breadth-First Search):
   * 廣度是盡可能的找同一層的結點
package utitled;

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

public class GraphDemo {

  public static void main(String[] args) {
    // 宣告所有頂點
    String[] vertexArr = {"A", "B", "C", "D", "E"};

    // 創建圖
    Graph graph = new Graph(vertexArr.length);
    // 新增頂點
    for (int i = 0; i < vertexArr.length; i++) {
      graph.addVertex(vertexArr[i]);
    }
    // 新增邊 AB AC BC BD BE
    // 無向圖 - 邊的關係是雙向的
    graph.addEdge(0, 1, 1);
    graph.addEdge(1, 0, 1);
    graph.addEdge(0, 2, 1);
    graph.addEdge(2, 0, 1);
    graph.addEdge(1, 2, 1);
    graph.addEdge(2, 1, 1);
    graph.addEdge(1, 3, 1);
    graph.addEdge(3, 1, 1);
    graph.addEdge(1, 4, 1);
    graph.addEdge(4, 1, 1);

    System.out.println("graph.getNumOfVertex() = " + graph.getNumOfVertex());
    System.out.println("graph.getNumOfEdge() = " + graph.getNumOfEdge());
    // 顯示圖
    graph.showGraph();

//    graph.dfs();
    graph.bfs();
  }
}

class Graph {
  /**
   * 用來存放頂點(vertex)
   */
  private List<String> vertexs;
  /**
   * 用來存放邊
   */
  private int[][] edges;
  /**
   * 頂點的數量
   */
  private int numOfVertex;
  /**
   * 邊的數量
   */
  private int numOfEdge;
  /**
   * 紀錄頂點是否被訪問過
   */
  private boolean[] isVisitedArr;


  public Graph(int numOfVertex) {
    this.vertexs = new ArrayList<>(numOfVertex);
    this.numOfVertex = this.vertexs.size();
    this.edges = new int[numOfVertex][numOfVertex];
    this.numOfEdge = 0;
    this.isVisitedArr = new boolean[numOfVertex];
  }

  public int getNumOfVertex() {
    return numOfVertex;
  }

  public int getNumOfEdge() {
    return numOfEdge;
  }

  public String getVertexValue(int index) {
    return vertexs.get(index);
  }
  /**
   * 取得當前頂點的第一個鄰居頂點索引
   * @param currentInx
   * @return
   */
  public int getFirstNeighborIndex(int currentInx) {
    for (int i = 0; i < vertexs.size(); i++) {
      if (edges[currentInx][i] == 1) {
        // 找到current vertex 連接的鄰居頂點
        return i;
      }
    }
    return -1;
  }

  /**
   * 取得當前頂點的第一個鄰居頂點的隔壁的頂點
   * @param currentInx
   * @param firstNeighborInx
   * @return
   */
  public int getFirstNeighborNextInx(int currentInx, int firstNeighborInx) {
    for (int i = firstNeighborInx + 1; i < vertexs.size(); i++) {
      if (edges[currentInx][i] == 1) {
        return  i;
      }
    }
    return -1;
  }

  /**
   * 深度優先搜尋(DFS: Depth-First Search):
   * 深度優先遍歷,從初始訪問點出發,初始訪問點可能有多個鄰接結點,
   * 深度優先的遍歷的策略是首先訪問第一個鄰接點,
   * 然後再以這個被訪問的鄰接結點作為初始訪問結點,
   * 訪問它的第一個鄰接結點,
   * 即每一次訪問完當前節點都會首先訪問當前節點的第一個鄰節點。類似迷宮回溯算法。
   * 深度是盡可能找下一層的結點
   * @param isVisitedArr
   * @param currentInx
   */
  private void dfs(boolean[] isVisitedArr, int currentInx) {
    System.out.print(getVertexValue(currentInx) + "->");
    // 將當前頂點設定被訪問過
    isVisitedArr[currentInx] = true;
    // 取得當前頂點的第一個鄰接頂點索引w
    int w = getFirstNeighborIndex(currentInx);
    while(w != -1) {
      // 代表頂點索引w與當前頂點有連接
      if (!isVisitedArr[w]) {
        // 頂點索引w沒有被訪問過,進行dfs
        dfs(isVisitedArr, w);
      }
      // 若頂點w已被訪問過,就找第一個鄰接頂點的隔壁
      w = getFirstNeighborNextInx(currentInx, w);
    }
    // 若當前頂點與頂點w沒有連接,離開
  }

  /**
   * 第一次循環是以第一個鄰接點作為基準點,
   * 就已經訪問完所有的與第一個結點相關聯的點,
   * 完整遍歷是為了訪問與第一個結點不關聯的結點
   */
  public void dfs() {
    for (int i = 0; i < getNumOfVertex(); i++) {
      if (!isVisitedArr[i]) {
        // 當前頂點沒被訪問過
        dfs(isVisitedArr, i);
      }
    }
  }

  public void bfs() {
    for (int i = 0; i < getNumOfVertex(); i++) {
      if (!isVisitedArr[i]) {
        // 當前頂點沒被訪問過
        bfs(isVisitedArr, i);
      }
    }
  }

  /**
   * 廣度優先搜尋(Breadth-First Search):
   * 廣度是盡可能的找同一層的結點
   * @param isVisitedArr
   * @param currentInx
   */
  private void bfs(boolean[] isVisitedArr, int currentInx) {
    // 紀錄頂點訪問的順序
    List<Integer> queue = new LinkedList<>();

    // 輸出當前頂點索引
    System.out.print(getVertexValue(currentInx) + "->");
    // 當前索引標註已訪問
    isVisitedArr[currentInx] = true;
    // 已訪問過的頂點加入Queue
    queue.add(currentInx);

    while (!queue.isEmpty()) {
      // 佇列不為空
      // pop出佇列頭索引(先進先出)
      int queueHeadInx = queue.remove(currentInx);
      // 鄰接頂點索引
      int w = getFirstNeighborIndex(queueHeadInx);
      while (w != -1) {

        // 搜尋佇列頭的第一個鄰接頂點有連接 && 未訪問過
        if (!isVisitedArr[w]) {
          System.out.print(getVertexValue(w) + "->");
          // 鄰接頂點標註已訪問
          isVisitedArr[w] = true;
          // 入queue
          queue.add(w);
        }
        w = getFirstNeighborNextInx(queueHeadInx, w);

      }
    }
  }
  /**
   * 新增頂點
   * @param vertex
   */
  public void addVertex(String vertex) {
    vertexs.add(vertex);
    numOfVertex++;
  }

  /**
   * AB AC BC BD BE
   * 新增有連接關係(=1)的頂點
   * 無連接為0
   * @param v1 頂點1索引
   * @param v2 頂點2索引
   * @param weight 權值
   */
  public void addEdge(int v1, int v2, int weight) {
    edges[v1][v2] = weight;
//    edges[v2][v1] = weight;
    // 邊的數量+1
    numOfEdge++;
  }

  /**
   * 顯示圖
   */
  public void showGraph() {
    for (int[] edge: edges) {
      System.out.println("Arrays.toString(edge) = " + Arrays.toString(edge));
    }
  }
}

如有敘述錯誤,還請不吝嗇留言指教,thanks!