Bidirectional Dijkstra

Bidirectional Dijkstra pathfinding algorithm — interactive grid visualization, pseudocode in five languages, and complexity reference.

Time O((V + E) log V) Weighted Informed

Implementation

function bidijkstraPathfind(grid, start, end, allowDiag) {
  const rows = grid.length;
  const cols = grid[0].length;
  const distA = Matrix(rows, cols, Infinity);
  const distB = Matrix(rows, cols, Infinity);
  const prevA = new Map();
  const prevB = new Map();
  const visitedA = new Set();
  const visitedB = new Set();
  const heapA = PriorityQueue("priority");
  const heapB = PriorityQueue("priority");
  distA[start[0]][start[1]] = 0;
  distB[end[0]][end[1]] = 0;
  priorityQueuePush(heapA, {
    priority: 0,
    row: start[0],
    col: start[1]
  });
  priorityQueuePush(heapB, {
    priority: 0,
    row: end[0],
    col: end[1]
  });
  let best = Infinity;
  let meet = null;
  while (priorityQueueSize(heapA) || priorityQueueSize(heapB)) {
    const chooseA = !priorityQueueSize(heapB) || priorityQueueSize(heapA) && priorityQueuePeekValue(heapA, "priority") <= priorityQueuePeekValue(heapB, "priority");
    const heap = chooseA ? heapA : heapB;
    const dist = chooseA ? distA : distB;
    const otherDist = chooseA ? distB : distA;
    const visited = chooseA ? visitedA : visitedB;
    const otherVisited = chooseA ? visitedB : visitedA;
    const prev = chooseA ? prevA : prevB;
    const tag = chooseA ? 'visit-a' : 'visit-b';
    const node = heap.pop();
    const currentKey = key(node.row, node.col);
    if (visited.has(currentKey)) continue;
    visited.add(currentKey);
    chooseA ? visitForwardNode(node.row, node.col) : visitBackwardNode(node.row, node.col);
    if (otherVisited.has(currentKey)) {
      const total = dist[node.row][node.col] + otherDist[node.row][node.col];
      if (total < best) {
        best = total;
        meet = currentKey;
      }
    }
    if (best < Infinity) {
      const minA = priorityQueuePeekValue(heapA, "priority", Infinity);
      const minB = priorityQueuePeekValue(heapB, "priority", Infinity);
      if (Math.min(minA, minB) >= best) break;
    }
    for (const [nr, nc] of getNeighbors(grid, node.row, node.col, allowDiag)) {
      const nd = dist[node.row][node.col] + stepCost(grid, node.row, node.col, nr, nc);
      if (nd < dist[nr][nc]) {
        dist[nr][nc] = nd;
        prev.set(key(nr, nc), [node.row, node.col]);
        heap.push({
          priority: nd,
          row: nr,
          col: nc
        });
        pushFrontier(nr, nc);
      }
    }
  }
  if (meet) {
    reconstructBidirectionalPath(prevA, prevB, meet, start, end);
  } else {
    reportNoPath();
  }
}