You are given a list of airline tickets
where tickets[i] = [from<sub>i</sub>, to<sub>i</sub>]
represent the departure and the arrival airports of one flight. Reconstruct the itinerary in order and return it.
All of the tickets belong to a man who departs from "JFK"
, thus, the itinerary must begin with "JFK"
. If there are multiple valid itineraries, you should return the itinerary that has the smallest lexical order when read as a single string.
["JFK", "LGA"]
has a smaller lexical order than ["JFK", "LGB"]
.You may assume all tickets form at least one valid itinerary. You must use all the tickets once and only once.
Example 1:
Input: tickets = [["MUC","LHR"],["JFK","MUC"],["SFO","SJC"],["LHR","SFO"]]
Output: ["JFK","MUC","LHR","SFO","SJC"]
Example 2:
Input: tickets = [["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]]
Output: ["JFK","ATL","JFK","SFO","ATL","SFO"]
Could you provide an algorithm to solve this problem efficiently? Consider edge cases and the time/space complexity of your solution.
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach for reconstructing an itinerary involves trying every single possible route we could take between airports. We start at the beginning and exhaustively explore each path until we find one that visits all the required airports in the correct order based on the specified rules.
Here's how the algorithm would work step-by-step:
def reconstruct_itinerary_brute_force(tickets):
all_itineraries = []
number_of_tickets = len(tickets)
def find_itineraries(current_airport, current_itinerary, used_tickets):
# If all tickets have been used, check the validity and save
if len(used_tickets) == number_of_tickets:
all_itineraries.append(current_itinerary[:])
return
for index, ticket in enumerate(tickets):
departure_airport, destination_airport = ticket
if departure_airport == current_airport and index not in used_tickets:
# Choose this flight and explore from the destination
current_itinerary.append(destination_airport)
used_tickets.add(index)
find_itineraries(destination_airport, current_itinerary, used_tickets)
# Backtrack: remove the flight to try other options
current_itinerary.pop()
used_tickets.remove(index)
# Start from 'JFK' and explore all paths
find_itineraries('JFK', ['JFK'], set())
if not all_itineraries:
return ['JFK']
# Find the lexicographically smallest itinerary
smallest_itinerary = sorted(all_itineraries)[0]
return smallest_itinerary
To reconstruct the itinerary, we'll use a special way to explore the connections between the cities. Instead of guessing and checking, we will cleverly trace a path by always picking the next city in alphabetical order, ensuring we visit all cities.
Here's how the algorithm would work step-by-step:
from collections import defaultdict
def reconstruct_itinerary(flights):
flight_map = defaultdict(list)
for start, end in flights:
flight_map[start].append(end)
for start in flight_map:
flight_map[start].sort()
itinerary = []
def depth_first_search(airport):
# While possible, keep visiting the next airport
while flight_map[airport]:
next_airport = flight_map[airport].pop(0)
depth_first_search(next_airport)
# Append to the beginning for correct order.
itinerary.insert(0, airport)
# Start at JFK as per instructions
depth_first_search("JFK")
return itinerary
Case | How to Handle |
---|---|
Empty input tickets array | Return an empty list if the tickets array is empty, as there's no itinerary to construct. |
Tickets array with a single ticket ["JFK", "XXX"] | The correct itinerary is ["JFK", "XXX"], which the algorithm should directly derive. |
No valid itinerary exists (disconnected graph) | The algorithm must be able to detect a situation where it gets stuck and cannot reach all destinations, returning an empty list or indicating an error. |
Multiple valid itineraries exist | The algorithm must ensure that among multiple valid itineraries, the lexicographically smallest one is returned by using a priority queue for airport selections. |
Tickets forming a cycle, making a complete tour impossible | The algorithm, employing a depth-first search, must eventually exhaust all valid paths before concluding that no complete itinerary is possible or employ post-order traversal. |
Input tickets contain the same departure and arrival (["AAA", "AAA"]) | The algorithm should handle this case correctly, considering it as a valid hop in the itinerary. |
Large input dataset causing stack overflow with recursion | The algorithm should consider iterative methods to avoid stack overflow exceptions and instead use a map to keep track of visited nodes and their next neighbors. |
All tickets start from JFK, and they lead to the same destination. | The algorithm should correctly handle duplicate tickets, which affect visit count and lexicographical order if priority queue is used for neighbors. |