Bus Routes
Problem
Given a list of bus routes where each route is a list of stops, and two stops source and target, return the minimum number of buses one must take to travel from source to target. Return -1 if it is not possible.
- 1 ≤ routes.length ≤ 500
- 1 ≤ routes[i].length ≤ 10⁵
- 0 ≤ routes[i][j] < 10⁶
- 0 ≤ source, target < 10⁶
Example
routes = [[1,2,7],[3,6,7]], source = 1, target = 62Starting at stop 1, take the first bus (route 0) which visits stops 1, 2, and 7. At stop 7, transfer to the second bus (route 1) which visits stops 3, 6, and 7. This requires taking 2 buses in total. The BFS explores stops reachable by taking buses from the source, expanding layer by layer until the target stop is found.
Approach
Straightforward Solution
A brute-force approach would attempt to explore all possible sequences of stops and buses, which is computationally infeasible due to the large input size and potential cycles.
Core Observation
The problem can be viewed as a graph traversal where nodes are bus stops and edges represent the ability to travel between stops via a bus route. The goal is to find the shortest path in terms of number of buses taken, not stops visited.
Path to Optimal
PreviewThe key insight is to use Breadth-First Search (BFS) starting from the source stop, exploring all stops reachable by taking one bus, then all stops reachable by taking two buses, and so forth…
Full step-by-step walkthrough on Pro →
Optimal Approach
PreviewConstruct a mapping from each stop to the list of buses that visit it. Use a BFS queue initialized with the source stop…
Full step-by-step walkthrough on Pro →
Want the full reasoning chain?
Unlock the complete walkthrough, line-by-line analysis, and recall drill.
Unlock ProTime
O(N + E)
Building the stop-to-bus mapping takes O(N) where N is the total number of stops across all routes. BFS explores each stop and bus at most once, resulting in O(N + E) time, where E is the total number of edges (bus-stop connections).
Space
O(N + E)
The stop-to-bus mapping and BFS queue require O(N + E) space, where N is the number of stops and E is the number of bus-stop edges. The visited sets for stops and buses also scale with input size.
Pattern Spotlight
BFS (Shortest Path on Unweighted Graph)
When the problem asks for the minimum number of steps or transitions in a network with uniform edge cost, model the problem as a graph and use BFS to explore layer by layer, ensuring the first time you reach the target is the shortest path.
Solution
| 1 | class Solution: |
| 2 | def numBusesToDestination(self, routes: List[List[int]], source: int, target: int) -> int: |
| 3 | if source == target: |
| 4 | return 0 |
| 5 | |
| 6 | stop_to_buses = defaultdict(list) |
| 7 | for bus, stops in enumerate(routes): |
| 8 | for stop in stops: |
| 9 | stop_to_buses[stop].append(bus) |
| 10 | |
| 11 | queue = deque([source]) |
| 12 | visited_stops = {source} |
| 13 | used_buses = set() |
| 14 | buses_taken = 0 |
| 15 | |
| 16 | while queue: |
| 17 | buses_taken += 1 |
| 18 | |
| 19 | for _ in range(len(queue)): |
| 20 | stop = queue.popleft() |
| 21 | |
| 22 | for bus in stop_to_buses[stop]: |
| 23 | if bus in used_buses: |
| 24 | continue |
| 25 | |
| 26 | used_buses.add(bus) |
| 27 | |
| 28 | for next_stop in routes[bus]: |
| 29 | if next_stop == target: |
| 30 | return buses_taken |
| 31 | |
| 32 | if next_stop not in visited_stops: |
| 33 | visited_stops.add(next_stop) |
| 34 | queue.append(next_stop) |
| 35 | |
| 36 | return -1 |
Step-by-Step Solution
Build Stop-to-Bus Mapping to Enable Fast Bus Lookup
| 6 | stop_to_buses = defaultdict(list) |
| 7 | for bus, stops in enumerate(routes): |
| 8 | for stop in stops: |
| 9 | stop_to_buses[stop].append(bus) |
Objective
To create a mapping from each stop to the list of buses that visit it, enabling quick lookup of buses available at any stop.
Key Insight
Directly searching which buses can be taken from a stop is expensive without indexing. By precomputing a dictionary mapping stops to buses, the algorithm can instantly find all buses accessible from any stop during BFS traversal, reducing redundant searches and improving efficiency.
Interview Quick-Check
Core Logic
The stop-to-bus mapping transforms the problem into a graph where edges connect stops to buses, enabling BFS to traverse stops via buses efficiently.
Common Pitfalls & Bugs
Failing to build this mapping leads to repeated scanning of all routes for each stop, causing timeouts.
Initialize BFS State with Source Stop and Tracking Structures
To set up the BFS queue starting from the source stop and initialize visited sets for stops and buses to avoid revisiting.
Perform BFS to Explore Stops Layer by Layer by Taking Buses
To iteratively explore stops reachable by taking an increasing number of buses, returning the bus count when the target stop is found.
Return -1 if Target Stop is Unreachable
To return -1 when BFS completes without finding the target stop, indicating no possible route.
3 more steps with full analysis available on Pro.
Line Analysis
This solution has 7 Critical lines interviewers watch for.
if next_stop == target:
Check if the next stop is the target stop.
Finding the target stop means the shortest path in terms of buses taken has been found, allowing immediate return.
return buses_taken
Return the current bus count as the minimum buses needed.
Returning immediately upon finding the target stop ensures the BFS shortest path property is preserved and the minimal bus count is returned.
stop_to_buses[stop].append(bus)
Append the current bus to the list of buses for this stop.
This line populates the stop-to-bus mapping, enabling quick access to buses from any stop during BFS.
Full line-by-line criticality + rationale for all 25 lines available on Pro.
Test Your Understanding
Why does BFS guarantee the minimum number of buses taken to reach the target stop?
See the answer with Pro.
Related Problems
BFS pattern
Don't just read it. Drill it.
Reconstruct Bus Routes from memory until it sticks. AlgoDrill blanks out key lines and makes you fill them back in, step by step.
Unlock the Bus Routes drill