-
Notifications
You must be signed in to change notification settings - Fork 2
feat(algorithms, graphs): reconstruct itinerary #144
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 2 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
0d9a4ce
feat(algorithms, graphs): reconstruct itinerary
BrianLusina c8e3464
updating DIRECTORY.md
07f174e
feat(algorithms, graphs): cat and mouse
BrianLusina 303bcb7
updating DIRECTORY.md
4123342
refactor(algorithms, graphs): equality check
BrianLusina 13f59f7
docs(algorithms, graphs): hyphenate depth-first search
BrianLusina 5e0f104
docs(algorithms, graphs): fix typo
BrianLusina File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| # Reconstruct Itinerary | ||
|
|
||
| Given a list of airline tickets where tickets[i] = [fromi, toi] represent a departure airport and an arrival airport of | ||
| a single flight, reconstruct the itinerary in the correct order and return it. | ||
|
|
||
| The person who owns these tickets always starts their journey from "JFK". Therefore, the itinerary must begin with "JFK". | ||
| If there are multiple valid itineraries, you should prioritize the one with the smallest lexical order when considering | ||
| a single string. | ||
|
|
||
| > Lexicographical order is a way of sorting similar to how words are arranged in a dictionary. It compares items | ||
| > character by character, based on their order in the alphabet or numerical value. | ||
|
|
||
| - For example, the itinerary ["JFK", "EDU"] has a smaller lexical order than ["JFK", "EDX"]. | ||
|
|
||
| > Note: You may assume all tickets form at least one valid itinerary. You must use all the tickets exactly once. | ||
|
|
||
| ## Constraints | ||
|
|
||
| - 1 <= `tickets.length` <= 300 | ||
| - `tickets[i].length` == 2 | ||
| - `fromi.length` == 3 | ||
| - `toi.length` == 3 | ||
| - `fromi` != `toi` | ||
| - `fromi` and `toi` consist of upper case English letters | ||
|
|
||
| ## Examples | ||
|
|
||
|  | ||
|  | ||
|  | ||
|
|
||
| ## Related Topics | ||
|
|
||
| - Depth First Search | ||
| - Graph | ||
| - Eulerian Circuit | ||
|
|
||
| ## Solution | ||
|
|
||
| The algorithm uses __Hierholzer’s algorithm__ to reconstruct travel itineraries from a list of airline tickets. This | ||
| problem is like finding an __Eulerian path__ but with a fixed starting point, “JFK”. Hierholzer’s algorithm is great for | ||
| finding Eulerian paths and cycles, which is why we use it here. | ||
|
|
||
| > Hierholzer's algorithm is a method for finding an Eulerian circuit (a cycle that visits every edge exactly once) in a | ||
| > graph. It starts from any vertex and follows edges until it returns to the starting vertex, forming a cycle. If there | ||
| > are any unvisited edges, it starts a new cycle from a vertex on the existing cycle that has unvisited edges and merges | ||
| > the cycles. The process continues until all edges are visited. | ||
|
|
||
| > An Eulerian path is a trail in a graph that visits every edge exactly once. An Eulerian path can exist only if exactly | ||
| > zero or two vertices have an odd degree. If there are exactly zero vertices with an odd degree, the path can form a | ||
| > circuit (Eulerian circuit), where the starting and ending points are the same. If there are exactly two vertices with | ||
| > an odd degree, the path starts at one of these vertices and ends at the other. | ||
|
|
||
| The algorithm starts by arranging the destinations in reverse lexicographical order to ensure we always choose the | ||
| smallest destination first. It then uses depth-first search (DFS) starting from “JFK” to navigate the flights. As it | ||
| explores each flight path, it builds the itinerary by appending each visited airport when there are no more destinations | ||
| to visit from that airport. Since the airports are added in reverse order during this process, the final step is to | ||
| reverse the list to get the correct itinerary. | ||
|
|
||
| The basic algorithm to solve this problem will be: | ||
|
|
||
| 1. Create a dictionary, `flight_map`, to store the flight information. Each key represents an airport; its corresponding | ||
| value is a list of destinations from that airport. | ||
| 2. Initialize an empty list, result, to store the reconstructed itinerary. | ||
| 3. Sort the destinations lexicographically in reverse order to ensure that the smallest destination is chosen first. | ||
| 4. Perform DFS traversal starting from the airport "JFK". | ||
| - Get the list of destinations for the current airport from flight_map. | ||
| - While there are destinations available: | ||
| - Pop the next_destination from destinations. | ||
| - Recursively explore all available flights starting from the popped next_destination, until all possible flights | ||
| have been considered. | ||
| - Append the current airport to the result list. | ||
| 5. Return the result list in reverse order to ensure the itinerary starts from the initial airport, "JFK", and proceeds | ||
| through the subsequent airports in the correct order. | ||
|
|
||
| Let’s look at the following illustration to get a better understanding of the solution: | ||
|
|
||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|
|
||
| ### Time Complexity | ||
|
|
||
| Each edge (flight) is traversed once during the DFS process in the algorithm, resulting in a complexity proportional to | ||
| the number of edges, ∣E∣. | ||
| Before DFS, the outgoing edges for each airport must be sorted. The sorting operation’s complexity depends on the input | ||
| graph’s structure. | ||
| In the worst-case scenario, such as a highly unbalanced graph (e.g., star-shaped), where one airport (e.g., JFK) | ||
| dominates the majority of flights, the sorting operation on this airport becomes highly expensive, possibly reaching N log N | ||
| complexity where `N = |E|/2` In a more balanced or average scenario, where each airport has a roughly equal number of | ||
| outgoing flights, the sorting operation complexity remains O(N log N) where N represents half of the total number of | ||
| edges divided by twice the number of airports O(|E|/2|V|). Thus, the algorithm’s overall complexity is O(|E|log|E/2|), | ||
| emphasizing the significance of the sorting operation in determining its performance. | ||
|
|
||
| ### Space Complexity | ||
|
|
||
| The space complexity is O(∣V∣+∣E∣), where ∣V∣ is the number of airports and ∣E∣ is the number of flights. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| from typing import List, DefaultDict | ||
| from collections import defaultdict | ||
|
|
||
|
|
||
| def find_itinerary(tickets: List[List[str]]) -> List[str]: | ||
| """ | ||
| Reconstructs the itinerary from a given list of lists of tickets. | ||
| Args: | ||
| tickets (list): list of lists of tickets | ||
| Returns: | ||
| List[str]: reconstructed tickets | ||
| """ | ||
| if len(tickets) == 0: | ||
| return [] | ||
|
|
||
| graph = {} | ||
| # Create a graph for each airport and keep list of airport reachable from it | ||
| for src, dst in tickets: | ||
| if src in graph: | ||
| graph[src].append(dst) | ||
| else: | ||
| graph[src] = [dst] | ||
|
|
||
| for src in graph.keys(): | ||
| graph[src].sort(reverse=True) | ||
| # Sort children list in descending order so that we can pop last element | ||
| # instead of pop out first element which is costly operation | ||
| stack = [] | ||
| res = [] | ||
| stack.append("JFK") | ||
| # Start with JFK as starting airport and keep adding the next child to traverse | ||
| # for the last airport at the top of the stack. If we reach to an airport from where | ||
| # we can't go further then add it to the result. This airport should be the last to go | ||
| # since we can't go anywhere from here. That's why we return the reverse of the result | ||
| # After this backtrack to the top airport in the stack and continue to traaverse it's children | ||
|
BrianLusina marked this conversation as resolved.
Outdated
|
||
|
|
||
| while len(stack) > 0: | ||
| elem = stack[-1] | ||
| if elem in graph and len(graph[elem]) > 0: | ||
| # Check if elem in graph as there may be a case when there is no out edge from an airport | ||
| # In that case it won't be present as a key in graph | ||
| stack.append(graph[elem].pop()) | ||
| else: | ||
| res.append(stack.pop()) | ||
| # If there is no further children to traverse then add that airport to res | ||
| # This airport should be the last to go since we can't anywhere from this | ||
| # That's why we return the reverse of the result | ||
| return res[::-1] | ||
|
|
||
|
|
||
| def find_itinerary_using_hierholzers(tickets: List[List[str]]) -> List[str]: | ||
| flight_map: DefaultDict[str, List[str]] = defaultdict(list) | ||
| result: List[str] = [] | ||
|
|
||
| # Populate the flight map with each departure and arrival | ||
| for departure, arrival in tickets: | ||
| flight_map[departure].append(arrival) | ||
|
|
||
| # Sort each list of destinations in reverse lexicographical order | ||
| for departure in flight_map: | ||
| flight_map[departure].sort(reverse=True) | ||
|
|
||
| def dfs_traversal( | ||
| current: str, flights: DefaultDict[str, List[str]], res: List[str] | ||
| ): | ||
| destinations = flights[current] | ||
|
|
||
| # Traverse all destinations in the order of their lexicographical sorting | ||
| while destinations: | ||
| # Pop the last destination from the list (smallest lexicographical order due to reverse sorting) | ||
| next_destination = destinations.pop() | ||
| # Recursively perform DFS on the next destination | ||
| dfs_traversal(next_destination, flights, res) | ||
|
|
||
| # Append the current airport to the result after all destinations are visited | ||
| res.append(current) | ||
|
|
||
| dfs_traversal("JFK", flight_map, result) | ||
|
|
||
| return result[::-1] | ||
Binary file added
BIN
+64.6 KB
...raphs/reconstruct_itinerary/images/examples/reconstruct_itinerary_example_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+70.2 KB
...raphs/reconstruct_itinerary/images/examples/reconstruct_itinerary_example_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+70 KB
...raphs/reconstruct_itinerary/images/examples/reconstruct_itinerary_example_3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+69.3 KB
...phs/reconstruct_itinerary/images/solutions/reconstruct_itinerary_solution_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+95.1 KB
...hs/reconstruct_itinerary/images/solutions/reconstruct_itinerary_solution_10.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+98.6 KB
...hs/reconstruct_itinerary/images/solutions/reconstruct_itinerary_solution_11.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+98.3 KB
...hs/reconstruct_itinerary/images/solutions/reconstruct_itinerary_solution_12.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+92.2 KB
...hs/reconstruct_itinerary/images/solutions/reconstruct_itinerary_solution_13.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+103 KB
...hs/reconstruct_itinerary/images/solutions/reconstruct_itinerary_solution_14.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+72 KB
...hs/reconstruct_itinerary/images/solutions/reconstruct_itinerary_solution_15.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+90.1 KB
...phs/reconstruct_itinerary/images/solutions/reconstruct_itinerary_solution_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+95.4 KB
...phs/reconstruct_itinerary/images/solutions/reconstruct_itinerary_solution_3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+97.1 KB
...phs/reconstruct_itinerary/images/solutions/reconstruct_itinerary_solution_4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+102 KB
...phs/reconstruct_itinerary/images/solutions/reconstruct_itinerary_solution_5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+102 KB
...phs/reconstruct_itinerary/images/solutions/reconstruct_itinerary_solution_6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+99.2 KB
...phs/reconstruct_itinerary/images/solutions/reconstruct_itinerary_solution_7.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+90.2 KB
...phs/reconstruct_itinerary/images/solutions/reconstruct_itinerary_solution_8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+90 KB
...phs/reconstruct_itinerary/images/solutions/reconstruct_itinerary_solution_9.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.