JavaScript Dijkstra算法是一種經典的計算最短路徑的算法,它可以在圖中尋找最短路徑,常用于路程規劃、數據路由等場景。
Dijkstra算法的核心在于使用一個優先隊列來維護待處理的節點,先處理與起點距離最短的節點。整個算法分為兩個步驟:路徑初始化和每次松弛操作。下面我們通過一個小例子來介紹一下具體的實現。
// 構建一個簡單的圖結構,使用鄰接表表示 const graph = { start: { A: 6, B: 2 }, A: { finish: 1 }, B: { A: 3, finish: 5 }, finish: {} }; // 初始化起點到其他節點的距離 const distances = { A: 6, B: 2, finish: Infinity }; // 初始化節點的前驅節點 const predecessors = { A: 'start', B: 'start', finish: null }; // 建立一個優先隊列,起點距離最小的放在隊首 const queue = ['B', 'A'];
上述代碼中,我們構建了一個簡單的圖結構,其中 start 表示起點,finish 表示終點,A、B 是中間節點。然后我們初始化了起點到其他節點的距離,起點到 A 的距離是 6,到 B 的距離是 2,到終點的距離是 Infinity,表示還未確定最短路徑。我們同時初始化了節點的前驅節點,都是起點。最后在建立一個優先隊列,起點距離最小的放在隊首,這里起點到 B 的距離最小,所以 B 在隊首。
接下來,我們來看一下每次松弛操作都做了哪些事情:
while (queue.length >0) { const node = queue.shift(); const neighbors = Object.keys(graph[node]); for (let i = 0; i< neighbors.length; i++) { const neighbor = neighbors[i]; const distance = distances[node] + graph[node][neighbor]; if (distance< distances[neighbor]) { distances[neighbor] = distance; predecessors[neighbor] = node; queue.unshift(neighbor); // 將已處理的節點放回隊列 } } }
首先,我們取出隊列中距離起點最近的節點。然后我們遍歷這個節點的所有鄰居,計算起點到鄰居的距離。如果這個距離比原來的距離更小,那么就更新距離和前驅節點。同時,我們將已經處理完的節點放回隊列,因為它有可能成為其他節點的鄰居,需要重新計算距離。
最后我們輸出起點到終點的最短路徑:
const path = ['finish']; let lastNode = 'finish'; while (predecessors[lastNode] !== 'start') { path.unshift(predecessors[lastNode]); lastNode = predecessors[lastNode]; } path.unshift('start'); console.log(path.join(' ->')); // 輸出 start ->B ->A ->finish
我們從終點依次向前走,直到回到起點。這樣我們就找到了一條從起點到終點的最短路徑。
Dijkstra算法的時間復雜度為 O(V^2),其中 V 表示節點的個數。在稠密圖中,可以使用堆優化算法將復雜度降為 O(E log V),其中 E 表示邊的數量。無論是哪種情況,Dijkstra算法都是求解最短路徑的經典算法之一。