Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
You are given an array routes representing bus routes where routes[i] is a bus route that the ith bus repeats forever.
routes[0] = [1, 5, 7], this means that the 0th bus travels in the sequence 1 -> 5 -> 7 -> 1 -> 5 -> 7 -> 1 -> ... forever.You will start at the bus stop source (You are not on any bus initially), and you want to go to the bus stop target. You can travel between bus stops by buses only.
Return the least number of buses you must take to travel from source to target. Return -1 if it is not possible.
Example 1:
Input: routes = [[1,2,7],[3,6,7]], source = 1, target = 6 Output: 2 Explanation: The best strategy is take the first bus to the bus stop 7, then take the second bus to the bus stop 6.
Example 2:
Input: routes = [[7,12],[4,5,15],[6],[15,19],[9,12,13]], source = 15, target = 12 Output: -1
Constraints:
1 <= routes.length <= 500.1 <= routes[i].length <= 105routes[i] are unique.sum(routes[i].length) <= 1050 <= routes[i][j] < 1060 <= source, target < 106Problem summary: You are given an array routes representing bus routes where routes[i] is a bus route that the ith bus repeats forever. For example, if routes[0] = [1, 5, 7], this means that the 0th bus travels in the sequence 1 -> 5 -> 7 -> 1 -> 5 -> 7 -> 1 -> ... forever. You will start at the bus stop source (You are not on any bus initially), and you want to go to the bus stop target. You can travel between bus stops by buses only. Return the least number of buses you must take to travel from source to target. Return -1 if it is not possible.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Hash Map
[[1,2,7],[3,6,7]] 1 6
[[7,12],[4,5,15],[6],[15,19],[9,12,13]] 15 12
minimum-costs-using-the-train-line)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #815: Bus Routes
class Solution {
public int numBusesToDestination(int[][] routes, int source, int target) {
if (source == target) {
return 0;
}
Map<Integer, List<Integer>> g = new HashMap<>();
for (int i = 0; i < routes.length; i++) {
for (int stop : routes[i]) {
g.computeIfAbsent(stop, k -> new ArrayList<>()).add(i);
}
}
if (!g.containsKey(source) || !g.containsKey(target)) {
return -1;
}
Deque<int[]> q = new ArrayDeque<>();
Set<Integer> visBus = new HashSet<>();
Set<Integer> visStop = new HashSet<>();
q.offer(new int[] {source, 0});
visStop.add(source);
while (!q.isEmpty()) {
int[] current = q.poll();
int stop = current[0], busCount = current[1];
if (stop == target) {
return busCount;
}
for (int bus : g.get(stop)) {
if (visBus.add(bus)) {
for (int nextStop : routes[bus]) {
if (visStop.add(nextStop)) {
q.offer(new int[] {nextStop, busCount + 1});
}
}
}
}
}
return -1;
}
}
// Accepted solution for LeetCode #815: Bus Routes
func numBusesToDestination(routes [][]int, source int, target int) int {
if source == target {
return 0
}
g := make(map[int][]int)
for i, route := range routes {
for _, stop := range route {
g[stop] = append(g[stop], i)
}
}
if g[source] == nil || g[target] == nil {
return -1
}
q := list.New()
q.PushBack([2]int{source, 0})
visBus := make(map[int]bool)
visStop := make(map[int]bool)
visStop[source] = true
for q.Len() > 0 {
front := q.Front()
q.Remove(front)
stop, busCount := front.Value.([2]int)[0], front.Value.([2]int)[1]
if stop == target {
return busCount
}
for _, bus := range g[stop] {
if !visBus[bus] {
visBus[bus] = true
for _, nextStop := range routes[bus] {
if !visStop[nextStop] {
visStop[nextStop] = true
q.PushBack([2]int{nextStop, busCount + 1})
}
}
}
}
}
return -1
}
# Accepted solution for LeetCode #815: Bus Routes
class Solution:
def numBusesToDestination(
self, routes: List[List[int]], source: int, target: int
) -> int:
if source == target:
return 0
g = defaultdict(list)
for i, route in enumerate(routes):
for stop in route:
g[stop].append(i)
if source not in g or target not in g:
return -1
q = [(source, 0)]
vis_bus = set()
vis_stop = {source}
for stop, bus_count in q:
if stop == target:
return bus_count
for bus in g[stop]:
if bus not in vis_bus:
vis_bus.add(bus)
for next_stop in routes[bus]:
if next_stop not in vis_stop:
vis_stop.add(next_stop)
q.append((next_stop, bus_count + 1))
return -1
// Accepted solution for LeetCode #815: Bus Routes
struct Solution;
use std::collections::HashMap;
use std::collections::HashSet;
use std::collections::VecDeque;
impl Solution {
fn num_buses_to_destination(routes: Vec<Vec<i32>>, s: i32, t: i32) -> i32 {
if s == t {
return 0;
}
let mut buses: HashMap<i32, HashSet<usize>> = HashMap::new();
for i in 0..routes.len() {
for &j in &routes[i] {
buses.entry(j).or_default().insert(i);
}
}
let mut queue: VecDeque<i32> = VecDeque::new();
let mut visited: HashSet<usize> = HashSet::new();
queue.push_back(s);
let mut res = 0;
while !queue.is_empty() {
let size = queue.len();
for _ in 0..size {
let u = queue.pop_front().unwrap();
if u == t {
return res;
}
let mut stops = HashSet::new();
for &bus in &buses[&u] {
if visited.insert(bus) {
for &i in &routes[bus] {
stops.insert(i);
}
}
}
for stop in stops {
queue.push_back(stop);
}
}
res += 1;
}
-1
}
}
#[test]
fn test() {
let routes = vec_vec_i32![[1, 2, 7], [3, 6, 7]];
let s = 1;
let t = 6;
let res = 2;
assert_eq!(Solution::num_buses_to_destination(routes, s, t), res);
}
// Accepted solution for LeetCode #815: Bus Routes
function numBusesToDestination(routes: number[][], source: number, target: number): number {
if (source === target) {
return 0;
}
const g: Map<number, number[]> = new Map();
for (let i = 0; i < routes.length; i++) {
for (const stop of routes[i]) {
if (!g.has(stop)) {
g.set(stop, []);
}
g.get(stop)!.push(i);
}
}
if (!g.has(source) || !g.has(target)) {
return -1;
}
const q: [number, number][] = [[source, 0]];
const visBus: Set<number> = new Set();
const visStop: Set<number> = new Set([source]);
for (const [stop, busCount] of q) {
if (stop === target) {
return busCount;
}
for (const bus of g.get(stop)!) {
if (!visBus.has(bus)) {
visBus.add(bus);
for (const nextStop of routes[bus]) {
if (!visStop.has(nextStop)) {
visStop.add(nextStop);
q.push([nextStop, busCount + 1]);
}
}
}
}
}
return -1;
}
Use this to step through a reusable interview workflow for this problem.
Two nested loops check every pair or subarray. The outer loop fixes a starting point, the inner loop extends or searches. For n elements this gives up to n²/2 operations. No extra space, but the quadratic time is prohibitive for large inputs.
Most array problems have an O(n²) brute force (nested loops) and an O(n) optimal (single pass with clever state tracking). The key is identifying what information to maintain as you scan: a running max, a prefix sum, a hash map of seen values, or two pointers.
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.