廣度優先,深度優先,尋求最短路徑。

clipboard.png

1、應用

  • 深度優先:是否存在通路,尋找全部解。
  • 廣度優先遍歷:尋求最優解,尋求最短路徑

1.鄰接矩陣JAVA代碼實現

鄰接矩陣可使用一個二維數組來表示java

public class GraphTest {

  // 節點
  public static class Vertex {
    public String name;
    private boolean isVisited;

    public Vertex(String name) {
      this.name = name;
      this.isVisited = false;
    }

    public void displayName() {
      System.out.println("name:" + name);
    }
  }

  // 圖
  public static class Graph {
    // 存節點數據
    private Vertex[] vertices;
    // 矩陣
    private int[][] matrix;
    // 隊列,用於BFS
    private Queue<Integer> queue = new LinkedList<>();
    // 棧,用於DFS
    private Stack<Integer> stack = new Stack<>();

    private Map<Integer, Integer> dependencyMap = new HashMap<>();

    public Graph(Vertex[] vertices, int[][] matrix) {
      this.vertices = vertices;
      this.matrix = matrix;
    }

    // 找到未訪問過的鄰接點
    public List<Integer> getUnvisitedVertex(int i) {
      List<Integer> unvisitedVertices = new ArrayList<>();
      for (int j = 0; j < matrix.length; j++) {
        if (matrix[i][j] > 0 && !vertices[j].isVisited) {
          unvisitedVertices.add(j);
        }
      }
      return unvisitedVertices;
    }

    // 廣度優先
    public void bfs(int vertex) {
      queue.offer(vertex);

      while (!queue.isEmpty()) {
        int v = queue.poll();
        if (!vertices[v].isVisited) {
          vertices[v].displayName();
          vertices[v].isVisited = true;

          List<Integer> unvisitedVertices = getUnvisitedVertex(v);
          unvisitedVertices.forEach(uv -> {
            queue.offer(uv);
            dependencyMap.putIfAbsent(uv, v);
          });
        }
      }
    }

    // 深度優先
    public void dfs(int vertex) {
      stack.push(vertex);

      while (!stack.isEmpty()) {
        int v = stack.pop();
        if (!vertices[v].isVisited) {
          vertices[v].displayName();
          vertices[v].isVisited = true;

          List<Integer> unvisitedVertices = getUnvisitedVertex(v);
          unvisitedVertices.forEach(uv -> stack.push(uv));
        }
      }
    }

    // 深度優先遞歸實現
    public void dfsRecursion(int vertex) {
      if (!vertices[vertex].isVisited) {
        vertices[vertex].displayName();
        vertices[vertex].isVisited = true;

        List<Integer> unvisitedVertices = getUnvisitedVertex(vertex);
        unvisitedVertices.forEach(this::dfsRecursion);
      }
    }

    public void printShortestPath(int start, int end) {
      bfs(start);

      String symbol = "-->";
      StringBuilder sb = new StringBuilder();
      sb.append(vertices[end].name);
      sb.append(symbol);
      while (dependencyMap.get(end) != null) {
        sb.append(vertices[dependencyMap.get(end)].name);
        sb.append(symbol);
        end = dependencyMap.get(end);
      }
      String path = sb.substring(0, sb.lastIndexOf(symbol));
      System.out.println(path);
    }

    public void clear() {
      stack.clear();
      queue.clear();
      dependencyMap.clear();
      for (int i = 0; i < vertices.length; i++) {
        vertices[i].isVisited = false;
      }
    }

  }

  public static void main(String[] args) {
    Vertex[] vertices = {
            new Vertex("v0"),
            new Vertex("v1"),
            new Vertex("v2"),
            new Vertex("v3"),
            new Vertex("v4")
    };

    int[][] matrix = new int[][]{
            {0, 0, 1, 1, 0},
            {0, 0, 1, 0, 1},
            {1, 1, 0, 0, 1},
            {1, 0, 0, 0, 1},
            {0, 1, 1, 1, 0}
    };

    Graph graph = new Graph(vertices, matrix);

    System.out.println("廣度優先搜索:");
    graph.bfs(0);
    graph.clear();

    System.out.println("深度優先搜索:");

    graph.dfs(0);
    graph.clear();

    System.out.println("遞歸深度優先搜索:");

    graph.dfsRecursion(0);
    graph.clear();

    System.out.println("打印最短路徑:");

    graph.printShortestPath(0, 4);
  }
}

打印結果
廣度優先搜索:
name:v0
name:v2
name:v3
name:v1
name:v4
深度優先搜索:
name:v0
name:v3
name:v4
name:v2
name:v1
遞歸深度優先搜索:
name:v0
name:v2
name:v1
name:v4
name:v3
打印最短路徑:
name:v0
name:v2
name:v3
name:v1
name:v4
v4-->v2-->v0node

2.鄰接表JAVA代碼實現

鄰接表可使用數組+鏈表,鏈表+鏈表,哈希表等等數據結構來表示。在java中,map結構很是適合表示鄰接表數組

public class GraphTest {
  public static void main(String[] args){
    //初始化先創建起各個節點信息,以及對應的直接子節點列表
    HashMap<String,String[]> map = new HashMap<>();
    map.put("V0", new String[] {"V2","V3"});
    map.put("V1", new String[] {"V2","V4"});
    map.put("V2", new String[] {"V0","V1","V4"});
    map.put("V3", new String[] {"V0","V4"});
    map.put("V4", new String[] {"V1","V2","V3"});
    //獲取從A到H的最短路徑節點鏈表
    Node target = findTarget("V0","V4",map);
    //打印出最短路徑的各個節點信息
    printSearPath(target);

  }

  /**
   * 打印出到達節點target所通過的各個節點信息
   * @param target
   */
  static void printSearPath(Node target) {
    if (target != null) {
      System.out.print("找到了目標節點:" + target.id + "\n");

      List<Node> searchPath = new ArrayList<Node>();
      searchPath.add(target);
      Node node = target.parent;
      while(node!=null) {
        searchPath.add(node);
        node = node.parent;
      }
      String path = "";
      for(int i=searchPath.size()-1;i>=0;i--) {
        path += searchPath.get(i).id;
        if(i!=0) {
          path += "-->";
        }
      }
      System.out.print("步數最短:"+path);
    } else {
      System.out.print("未找到了目標節點");
    }
  }

  /**
   * 從指定的開始節點 startId ,查詢到目標節點targetId 的最短路徑
   * @param startId
   * @param targetId
   * @param map
   * @return
   */
  static Node findTarget(String startId,String targetId,HashMap<String,String[]> map) {
    List<String> hasSearchList = new ArrayList<String>();
    LinkedList<Node> queue = new LinkedList<Node>();
    queue.offer(new Node(startId,null));
    while(!queue.isEmpty()) {
      Node node = queue.poll();

      if(hasSearchList.contains(node.id)) {
        //跳過已經搜索過的,避免重複或者死循環
        continue;
      }
      System.out.print("判斷節點:" + node.id +"\n");
      if (targetId.equals(node.id)) {
        return node;
      }
      hasSearchList.add(node.id);
      if (map.get(node.id) != null && map.get(node.id).length > 0) {
        for (String childId : map.get(node.id)) {
          queue.offer(new Node(childId,node));
        }
      }
    }

    return null;
  }

  /**
   * 節點對象
   * @author Administrator
   *
   */
  static class Node{
    //節點惟一id
    public String id;
    //該節點的直接父節點
    public Node parent;
    //該節點的直接子節點
    public List<Node> childs = new ArrayList<Node>();
    public Node(String id,Node parent) {
      this.id = id;
      this.parent = parent;
    }
  }
}

打印:
判斷節點:V0
判斷節點:V2
判斷節點:V3
判斷節點:V1
判斷節點:V4
找到了目標節點:V4
步數最短:V0-->V2-->V4數據結構

相關文章
相關標籤/搜索