首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 圖論演算法:廣度優先搜尋 (BFS) 計算最短路徑完整實現

     編輯:Java 相關知識

問題背景

在無權無向圖中尋找兩點間最短路徑(邊數最少),BFS 是最優選擇。時間複雜度 O(V+E),空間 O(V)。

圖的表示:鄰接表


public class Graph {
    private String startVertex;
    private final Map> adj = new HashMap<>();
    private final Algorithm algorithm; // 策略模式注入演算法
    
    public Graph(Algorithm algorithm) { this.algorithm = algorithm; }
    
    public void addVertex(String v) { adj.putIfAbsent(v, new ArrayList<>()); }
    
    public void addEdge(String from, String to) {
        if (startVertex == null) startVertex = from;
        adj.get(from).add(to);
        adj.get(to).add(from); // 無向圖
    }
    
    public Map> getAdj() { return adj; }
    public String getStartVertex() { return startVertex; }
    
    // 執行演算法
    public void execute() { algorithm.perform(this, startVertex); }
    
    // 根據前驅映射重構路徑
    public Stack findPathTo(String target) {
        Stack path = new Stack<>();
        path.push(target);
        Map parent = algorithm.getPath();
        for (String v = parent.get(target); !v.equals(startVertex); v = parent.get(v)) {
            path.push(v);
        }
        path.push(startVertex);
        return path;
    }
}

演算法介面設計(策略模式)


public interface Algorithm {
    void perform(Graph g, String source);
    Map getPath(); // 返回 child -> parent 映射
}

BFS 核心實現


public class BFSAlgorithm implements Algorithm {
    private final List visited = new ArrayList<>();
    private final Map parent = new HashMap<>(); // child -> parent
    
    @Override
    public void perform(Graph g, String source) {
        visited.clear();
        parent.clear();
        
        Queue queue = new LinkedList<>();
        visited.add(source);
        queue.offer(source);
        
        while (!queue.isEmpty()) {
            String current = queue.poll();
            for (String neighbor : g.getAdj().get(current)) {
                if (!visited.contains(neighbor)) {
                    visited.add(neighbor);
                    parent.put(neighbor, current); // 記錄前驅
                    queue.offer(neighbor);
                }
            }
        }
    }
    
    @Override
    public Map getPath() { return parent; }
}

BFS 執行流程圖解

圖結構:
North Gate -- Classroom -- Toilet -- South Gate
     |            |           |
     |            |           |
   Square ----- Canteen      |
     |                        |
     +------------------------+

起點: North Gate
目標: Canteen

BFS 展開:
層 0: [North Gate]
層 1: [Classroom, Square]        <- 從 North Gate 可達
層 2: [Toilet, Canteen]          <- 從 Classroom/Square 可達
找到 Canteen!路徑長度 = 2

前驅映射:
Classroom -> North Gate
Square -> North Gate
Toilet -> Classroom (或 Square)
Canteen -> Square

路徑重構:Canteen -> Square -> North Gate

完整測試用例


public class BFSTest {
    @Test
    public void testShortestPath() {
        String[] vertices = {"North Gate", "South Gate", "Classroom", "Square", "Toilet", "Canteen"};
        String[][] edges = {
            {"North Gate", "Classroom"},
            {"North Gate", "Square"},
            {"Classroom", "Toilet"},
            {"Square", "Toilet"},
            {"Square", "Canteen"},
            {"Toilet", "South Gate"}
        };
        
        Graph g = new Graph(new BFSAlgorithm());
        Arrays.stream(vertices).forEach(g::addVertex);
        for (String[] e : edges) g.addEdge(e[0], e[1]);
        
        g.execute();
        Stack path = g.findPathTo("Canteen");
        
        System.out.print("BFS: From [North Gate] to [Canteen]: ");
        while (!path.isEmpty()) System.out.print(path.pop() + " ");
        // 輸出: North Gate Square Canteen
    }
}

BFS vs DFS vs Dijkstra 選擇指南

演算法適用圖類型最短路徑定義時間複雜度空間
BFS無權無向/有向邊數最少O(V+E)O(V)
DFS無權非最短(任意路徑)O(V+E)O(V)
Dijkstra帶權非負權重和最小O(E log V)O(V)
Bellman-Ford帶權含負權權重和最小O(VE)O(V)
A*帶權+啟發式權重和最小視啟發式O(V)

優化與變體

雙向 BFS(大圖優化)


// 同時從起點和終點展開,相遇時結束
// 空間/時間約減半,適用於社交網絡六度分隔等大圖
Queue qStart = new LinkedList<>(), qEnd = new LinkedList<>();
Set vStart = new HashSet<>(), vEnd = new HashSet<>();
Map pStart = new HashMap<>(), pEnd = new HashMap<>();

qStart.offer(start); vStart.add(start);
qEnd.offer(end); vEnd.add(end);

while (!qStart.isEmpty() && !qEnd.isEmpty()) {
    // 總是展開較小的隊列
    if (expandOneLevel(qStart, vStart, vEnd, pStart, g)) return buildPath(pStart, pEnd, meetPoint);
    if (expandOneLevel(qEnd, vEnd, vStart, pEnd, g)) return buildPath(pStart, pEnd, meetPoint);
}

0-1 BFS(權重僅 0/1)

    雙端隊列:權重 0 推入頭部,權重 1 推入尾部,O(V+E) 求最短路徑

總結

BFS 是無權圖最短路徑的標準解法,配合鄰接表 + 隊列 + 前驅映射即可高效實現。策略模式解耦圖與演算法,便於擴展 Dijkstra、A* 等。大圖場景考慮雙向 BFS 或啟發式搜索。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved