Bidirectional BFS

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

Time O(V + E) Unweighted Uniform

Implementation

function bidirectionalPathfind(grid, start, end, allowDiag) {
  const startKey = key(start[0], start[1]);
  const endKey = key(end[0], end[1]);
  const visitedA = new Map([[startKey, null]]);
  const visitedB = new Map([[endKey, null]]);
  let frontierA = [[start[0], start[1]]];
  let frontierB = [[end[0], end[1]]];
  while (frontierA.length && frontierB.length) {
    const nextA = [];
    for (const [cr, cc] of frontierA) {
      visitForwardNode(cr, cc);
      const currentKey = key(cr, cc);
      if (visitedB.has(currentKey)) {
        reconstructBidirectionalPath(visitedA, visitedB, currentKey, start, end);
        return;
      }
      for (const [nr, nc] of getNeighbors(grid, cr, cc, allowDiag)) {
        const neighborKey = key(nr, nc);
        if (visitedA.has(neighborKey)) continue;
        visitedA.set(neighborKey, [cr, cc]);
        nextA.push([nr, nc]);
        pushFrontier(nr, nc);
      }
    }
    frontierA = nextA;
    const nextB = [];
    for (const [cr, cc] of frontierB) {
      visitBackwardNode(cr, cc);
      const currentKey = key(cr, cc);
      if (visitedA.has(currentKey)) {
        reconstructBidirectionalPath(visitedA, visitedB, currentKey, start, end);
        return;
      }
      for (const [nr, nc] of getNeighbors(grid, cr, cc, allowDiag)) {
        const neighborKey = key(nr, nc);
        if (visitedB.has(neighborKey)) continue;
        visitedB.set(neighborKey, [cr, cc]);
        nextB.push([nr, nc]);
        pushFrontier(nr, nc);
      }
    }
    frontierB = nextB;
  }
  reportNoPath();
}