Reconstruct Itinerary
Reconstruct Itinerary: use every airline ticket once to build the lexicographically smallest itinerary starting at JFK.
- 1 <= tickets.length <= 300
- tickets[i].length == 2
- fromi.length == 3
- toi.length == 3
- fromi and toi consist of uppercase English letters.
- fromi != toi
Intuition
Reconstruct itinerary uses every airline ticket exactly once to build the lexicographically smallest route starting at JFK. Two things make it harder than it first appears. Greedily taking the smallest available destination at each step can strand you. You may reach an airport with no unused tickets while tickets elsewhere remain, and since every ticket must be used, that route is invalid — but you have already committed to it. The correct framing is that tickets are directed edges and the itinerary must use each exactly once. That is an Eulerian path, and the algorithm for it is Hierholzer's: - Follow edges greedily until stuck, then record airports on the way back out of the recursion. The insight is that getting stuck is not a failure. In a graph with an Eulerian path, the airport where you run out of tickets must be the final destination — no other vertex can strand you, because every other vertex has balanced in and out degrees. So appending airports as the recursion unwinds naturally places dead ends at the end of the route. That produces the path in reverse, so reverse it once at the finish. Lexicographic order comes free: always consume the smallest available destination first. A min-heap per airport, or a sorted list consumed from the front, handles that. Duplicate tickets between the same pair must stay as separate entries, since each is a distinct ticket that must be used.
The phrase 'use every ticket/edge exactly once' signals an Eulerian trail rather than ordinary path search. When lexical minimality is also required, order each vertex's outgoing edges while running Hierholzer's algorithm.
Approach
Before reading on: price up what the direct approach costs here, then ask what one visit per cell or vertex would have to mark so nothing is counted twice. Aim for O(E log E) time and O(E) space.
See why greedy alone fails
Taking the smallest destination each step can leave you stranded with tickets unused. The route must consume every ticket, so a dead end reached too early is invalid — which is what rules out a plain greedy walk.
Recognise it as an Eulerian path
Tickets are directed edges to be used exactly once, which is the definition of an Eulerian path. Naming it points straight at Hierholzer's algorithm rather than an ad-hoc search.
Store destinations in sorted order
Keep a min-heap or sorted list per airport so the smallest unused destination is always taken first. Duplicate tickets stay as separate entries — two tickets from A to B must both be consumed.
Consume tickets before recursing
Remove the ticket from the structure before the recursive call, not after. Leaving it in place lets the same ticket be used twice and produces a route longer than the ticket count.
Append on the way out
Add the current airport to the route only once it has no tickets left. A stuck airport must be the final destination in a graph with an Eulerian path, so post-order placement puts dead ends in exactly the right spot.
Reverse the result
The post-order construction builds the itinerary from the end backwards, so reverse it once at the finish. The reversed list starts at JFK and uses every ticket in lexically smallest order.
Cost of the algorithm
Sorting or heapifying the destinations costs O(E log E), and each edge is traversed exactly once, giving O(E log E) time overall with O(E) space for the adjacency structure and the route.
Solution & live demo
Common pitfalls
Appending before exploring
route.append(airport) visit(next_airport)
visit(next_airport) route.append(airport)
Eulerian construction records vertices on backtracking so premature dead ends land at the end.
Using a set for destinations
graph[source].add(destination)
heappush(graph[source], destination)
A set destroys duplicate tickets, even though each duplicate must be consumed.
Returning postorder directly
return route
return route[::-1]
Airports are appended from the route's end back toward JFK.
Edge cases
Each heap insertion is a separate edge and is popped exactly once.
Postorder insertion delays that dead end instead of invalidating the remaining tour.
DFS appends the destination then JFK, and reversal returns both airports.