332. Reconstruct Itinerary

https://leetcode.com/problems/reconstruct-itinerary/

Given a list of airline tickets represented by pairs of departure and arrival airports [from, to], reconstruct the itinerary in order. All of the tickets belong to a man who departs from JFK. Thus, the itinerary must begin with JFK.
Note:
  1. If there are multiple valid itineraries, you should return the itinerary that has the smallest lexical order when read as a single string. For example, the itinerary ["JFK", "LGA"] has a smaller lexical order than ["JFK", "LGB"].
  2. All airports are represented by three capital letters (IATA code).
  3. You may assume all tickets form at least one valid itinerary.
Example 1:
Input: [["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]]
Output: ["JFK", "MUC", "LHR", "SFO", "SJC"]
Example 2:
Input: [["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]]
Output: ["JFK","ATL","JFK","SFO","ATL","SFO"]
Explanation: Another possible reconstruction is ["JFK","SFO","ATL","JFK","ATL","SFO"].
             But it is larger in lexical order.
--- ---
class Solution {
public List<String> findItinerary(List<List<String>> tickets) {
List<String> ans = new ArrayList<String>();
// Priority Queue to order lexicographically, and remove once used
Map<String, PriorityQueue<String>> graph = new HashMap<String, PriorityQueue<String>>();
buildGraph(tickets, graph);
// Post Order DFS => topological sort
dfs(graph, "JFK", ans);
return ans;
}
private void dfs(Map<String, PriorityQueue<String>> graph, String u, List<String> ans) {
// Greedy DFS .. no backtracking
// Note - Repeat expression graph.get(u),
// Do not use variable for graph.get(u), because PQ.poll() empties the graph
while (graph.get(u) != null && !graph.get(u).isEmpty()) {
dfs(graph, graph.get(u).poll(), ans);
}
// Post Order DFS
// Add first element to avoid Collections reverse in the end
ans.add(0, u);
}
private void buildGraph(List<List<String>> tickets, Map<String, PriorityQueue<String>> graph) {
for (List<String> edge: tickets) {
String u = edge.get(0);
String v = edge.get(1);
graph.putIfAbsent(u, new PriorityQueue<String>());
graph.get(u).offer(v);
}
}
}
class Solution {
public List<String> findItinerary(List<List<String>> tickets) {
List<String> ans = new ArrayList<String>();
if (tickets == null || tickets.size() == 0) {
return ans;
}
Map<String, PriorityQueue<String>> graph = new HashMap<String, PriorityQueue<String>>();
for (List<String> trip: tickets) {
String from = trip.get(0);
String to = trip.get(1);
PriorityQueue<String> destination = graph.getOrDefault(from, new PriorityQueue<String>());
destination.add(to);
graph.put(from, destination);
}
Stack<String> stack = new Stack<String>();
// Starting node
stack.push("JFK");
// Nodes still to be visited
while (!stack.isEmpty()) {
// Destination nodes exist
while (graph.get(stack.peek()) != null && !graph.get(stack.peek()).isEmpty()) {
// Priority Queue - try min lexicographical first, and discard after use
stack.push(graph.get(stack.peek()).poll());
}
// Add to first position to avoid Collections reverse in the end
ans.add(0, stack.pop());
}
return ans;
}
}