mirror of
https://github.com/prdlk/leetcode.git
synced 2026-11-02 13:12:44 +00:00
122 lines
3.1 KiB
Plaintext
122 lines
3.1 KiB
Plaintext
---
|
||
title: Breadth-First Search
|
||
---
|
||
|
||
# Breadth-First Search (BFS)
|
||
|
||
## The idea
|
||
|
||
You want a mango seller. You ask your friends first.
|
||
No luck? You ask friends-of-friends. Then their friends.
|
||
You search in **rings**, closest first.
|
||
|
||
That is BFS. It answers two questions:
|
||
1. **Is there a path** from A to B?
|
||
2. **What is the shortest path?** (unweighted — fewest hops)
|
||
|
||
BFS is *always* shortest path on unweighted graphs. First time you reach a node = fewest possible steps.
|
||
|
||
## The picture
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
YOU((You)) --> A((Alice))
|
||
YOU --> B((Bob))
|
||
A --> C((Claire))
|
||
A --> D((Dan))
|
||
B --> E((Eve))
|
||
E --> M((Mango seller ✓))
|
||
|
||
style YOU fill:#1565c0,color:#fff
|
||
style A fill:#6a1b9a,color:#fff
|
||
style B fill:#6a1b9a,color:#fff
|
||
style C fill:#ef6c00,color:#fff
|
||
style D fill:#ef6c00,color:#fff
|
||
style E fill:#ef6c00,color:#fff
|
||
style M fill:#2e7d32,color:#fff
|
||
```
|
||
|
||
Blue = start. Purple = ring 1. Orange = ring 2. Green = found at ring 3.
|
||
|
||
## The two tools
|
||
|
||
1. **Queue** — first in, first out. This keeps the rings in order.
|
||
2. **Visited set** — never check the same node twice. Without it, cycles loop forever.
|
||
|
||
## The template
|
||
|
||
```python
|
||
from collections import deque
|
||
|
||
def bfs(start, graph: dict) -> None:
|
||
queue = deque([start])
|
||
visited = {start}
|
||
|
||
while queue:
|
||
level_size = len(queue) # snapshot = one full ring
|
||
for _ in range(level_size):
|
||
node = queue.popleft()
|
||
# process node here
|
||
for nxt in graph.get(node, []):
|
||
if nxt not in visited:
|
||
visited.add(nxt) # mark WHEN queued, not when popped
|
||
queue.append(nxt)
|
||
```
|
||
|
||
## Multi-source BFS (LC 994 — Rotting Oranges)
|
||
|
||
Start with *all* rotten oranges in the queue at once.
|
||
Each ring = one minute of rot spreading.
|
||
|
||
```python
|
||
from collections import deque
|
||
|
||
def oranges_rotting(grid: list[list[int]]) -> int:
|
||
rows, cols = len(grid), len(grid[0])
|
||
queue = deque()
|
||
fresh = 0
|
||
|
||
for r in range(rows):
|
||
for c in range(cols):
|
||
if grid[r][c] == 2:
|
||
queue.append((r, c))
|
||
elif grid[r][c] == 1:
|
||
fresh += 1
|
||
|
||
minutes = 0
|
||
dirs = [(1, 0), (-1, 0), (0, 1), (0, -1)]
|
||
|
||
while queue and fresh > 0:
|
||
for _ in range(len(queue)):
|
||
r, c = queue.popleft()
|
||
for dr, dc in dirs:
|
||
nr, nc = r + dr, c + dc
|
||
if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == 1:
|
||
grid[nr][nc] = 2
|
||
fresh -= 1
|
||
queue.append((nr, nc))
|
||
minutes += 1
|
||
|
||
return minutes if fresh == 0 else -1
|
||
```
|
||
|
||
## Complexity
|
||
|
||
| | Time | Space |
|
||
|---|---|---|
|
||
| BFS | O(V + E) | O(V) |
|
||
|
||
V = nodes, E = edges. On a grid: O(rows × cols).
|
||
|
||
## Close-out ritual
|
||
|
||
Before you submit, say out loud:
|
||
1. Time and space complexity.
|
||
2. One edge case trace: empty graph, start = target, or unreachable target.
|
||
|
||
## Plan problems
|
||
|
||
**Tree BFS A-set:** LC 102 · 199 · 1448
|
||
**Graph BFS A-set:** LC 994 · 127
|
||
**B-set:** LC 103 · 637 · 909 · 433 · 286
|