Codepath

Number of Flights

Unit 10 Session 1 Advanced (Click for link to problem statements)

Problem Highlights

  • 💡 Difficulty: Medium
  • Time to complete: 20-30 mins
  • 🛠️ Topics: Graphs, Breadth First Search (BFS), Shortest Path

1: U-nderstand

Understand what the interviewer is asking for by using test cases and questions about the problem.

  • Established a set (2-3) of test cases to verify their own solution later.
  • Established a set (1-2) of edge cases to verify their solution handles complexities.
  • Have fully understood the problem and have no clarifying questions.
  • Have you verified any Time/Space Constraints for this problem?
  • Q: What does the adjacency matrix flights[i][j] = 1 mean?
    • A: It means that there is a direct flight from airport i to airport j.
  • Q: What traversal method should we use to find the shortest path?
    • A: We should use Breadth First Search (BFS) since it explores all possible paths layer by layer and guarantees finding the shortest path in an unweighted graph.
  • Q: What should we return if there is no path between the two airports?
    • A: Return -1 if no path exists.
HAPPY CASE
Input: flights = [
                  [0, 1, 0, 0],  # Airport 0
                  [0, 0, 1, 0],  # Airport 1
                  [0, 0, 0, 1],  # Airport 2
                  [0, 0, 0, 0]   # Airport 3
                 ]
start = 0
destination = 3
Output: 3
Explanation: The shortest path from airport 0 to airport 3 requires 3 flights (0 -> 1 -> 2 -> 3).

EDGE CASE
Input: flights = [
                  [0, 0, 0, 0],  # Airport 0
                  [0, 0, 1, 0],  # Airport 1
                  [0, 0, 0, 1],  # Airport 2
                  [0, 0, 0, 0]   # Airport 3
                 ]
start = 0
destination = 3
Output: -1
Explanation: There is no valid path from airport 0 to airport 3.

2: M-atch

Match what this problem looks like to known categories of problems, e.g. Linked List or Dynamic Programming, and strategies or patterns in those categories.

For Shortest Path problems, we want to consider the following approaches:

  • Breadth First Search (BFS): Since BFS explores all nodes level by level, it is ideal for finding the shortest path (minimum number of flights) in an unweighted graph.

3: P-lan

Plan the solution with appropriate visualizations and pseudocode.

General Idea: We will use BFS to find the shortest path between the starting and destination airports. BFS explores all airports layer by layer, ensuring that we find the shortest path (minimum number of flights).

1) Edge case: If the start airport is the same as the destination, return `0` (no flights needed).
2) Initialize a queue with `(start_airport, num_flights)` and a set `visited` to track the visited airports.
3) Perform BFS:
   a) Dequeue the current airport and number of flights.
   b) If the current airport is the destination, return the number of flights.
   c) For each neighboring airport (next_airport), if there is a direct flight and the neighbor hasn't been visited, add it to the queue and mark it as visited.
4) If BFS completes without finding the destination, return `-1`.

⚠️ Common Mistakes

  • Forgetting to check whether the current airport is the destination after each BFS step.
  • Not marking airports as visited, which can lead to infinite loops or revisiting airports unnecessarily.

4: I-mplement

Implement the code to solve the algorithm.

from collections import deque

def counting_flights(flights, start, destination):
    n = len(flights)

    # Step 1: Edge case - if start and destination are the same, no num_flights needed
    if start == destination:
        return 0

    # Step 2: Initialize BFS structures
    queue = deque([(start, 0)])  # (current airport, current number of num_flights)
    visited = set([start])

    # Step 3: BFS loop
    while queue:
        current_airport, num_flights = queue.popleft()

        # Explore neighbors (direct flights from current airport)
        for next_airport in range(n):
            if flights[current_airport][next_airport] == 1 and next_airport not in visited:
                if next_airport == destination:
                    return num_flights + 1 
                queue.append((next_airport, num_flights + 1))
                visited.add(next_airport)

    # Step 4: If we complete BFS without finding the destination, return -1
    return -1

5: R-eview

Review the code by running specific example(s) and recording values (watchlist) of your code's variables along the way.

  • Input:
    flights = [
              [0, 1, 0, 0],  # Airport 0
              [0, 0, 1, 0],  # Airport 1
              [0, 0, 0, 1],  # Airport 2
              [0, 0, 0, 0]   # Airport 3
             ]
    print(counting_flights(flights, 0, 3))  # Output: 3
    
    flights = [
              [0, 0, 0, 0],  # Airport 0
              [0, 0, 1, 0],  # Airport 1
              [0, 0, 0, 1],  # Airport 2
              [0, 0, 0, 0]   # Airport 3
             ]
    print(counting_flights(flights, 0, 3))  # Output: -1

6: E-valuate

Evaluate the performance of your algorithm and state any strong/weak or future potential work.

  • Time Complexity: O(n^2), where n is the number of airports. BFS explores all possible paths, and for each airport, we check all its neighbors.
  • Space Complexity: O(n) for the queue and visited set, as we store up to n airports.
Fork me on GitHub