Leetcode 332. Reconstruct Itinerary

来源:互联网 发布:windows显示后缀名 编辑:程序博客网 时间:2024/06/08 16:58

332. Reconstruct Itinerary

Total Accepted: 19256 Total Submissions: 71899 Difficulty: Medium

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:
tickets = [["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]]
Return ["JFK", "MUC", "LHR", "SFO", "SJC"].

Example 2:
tickets = [["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]]
Return ["JFK","ATL","JFK","SFO","ATL","SFO"].
Another possible reconstruction is ["JFK","SFO","ATL","JFK","ATL","SFO"]. But it is larger in lexical order.

思路:

DFS。

一开始想着用prorityQueue存着每个机场可以到达的点,然后遍历一遍,之后直接删,这样就可可以保证顺序。直到碰到这个test case:

Input:[["JFK","KUL"],["JFK","NRT"],["NRT","JFK"]]

Output:["JFK","KUL"]

Expected:["JFK","NRT","JFK","KUL"]

也就是说,先到了KUL,然后删了KUL,然后就不对了。应该是去NRT然后最后KUL。也就是说算法必须智能的恢复数据。那么肯定就是dfs + backtracking了。

一般DFS是用于输出所有可能性,于是先写了一个标准的DFS,把所有满足长度的结果存起来,然后输出result中的第一个结果。注意的地方是这里不能用prioritQueue了,因为每次我们遍历next 机场,完了之后还要相应复位。如果用priorityQueue,那么add之后位置就不确定了。可以用arrayList保存然后sort这样保证第一个结果一定是lexical order满足的。

public class Solution { // MLE    List<List<String>> res = new ArrayList<List<String>>();        public List<String> findItinerary(String[][] tickets) {        Map<String, ArrayList<String>> map = new HashMap<String, ArrayList<String>>();        for(int i = 0; i < tickets.length; i++){            String[] item = tickets[i];            if(!map.containsKey(item[0])){                map.put(item[0], new ArrayList<String>());            }            map.get(item[0]).add(item[1]);        }                for(ArrayList<String> al : map.values()) Collections.sort(al);                List<String> path = new ArrayList<String>(); path.add("JFK");        dfs(map, path, tickets.length + 1);        return res.get(0);    }        public void dfs(Map<String, ArrayList<String>> map, List<String> path, int tarLen){        if(path.size() == tarLen){            res.add(new ArrayList<String>(path)); return; // 一定要新建一个!!debug 半小时        }                String cur = path.get(path.size() - 1);        ArrayList<String> dest= map.get(cur);                for(int i = 0; dest != null && i < dest.size(); i++){            String next = dest.get(i);             path.add(next);            dest.remove(i);            dfs(map, path, tarLen);            path.remove(path.size() - 1);            dest.add(i, next);        }    }}// [["JFK","KUL"],["JFK","NRT"],["NRT","JFK"]] 访问K,回溯,访问N J 又要访问K

提示MLE。好吧,那剪枝一下,第一个满足条件的输出就行了。这样DFS就不能再是void了,因为在每个call的时候,需要知道子call是不是搞定了,搞定就不用再call了。

改成返回boolean就好。

public class Solution { // 14ms    public List<String> findItinerary(String[][] tickets) {        Map<String, ArrayList<String>> map = new HashMap<String, ArrayList<String>>();        for(int i = 0; i < tickets.length; i++){            String[] item = tickets[i];            if(!map.containsKey(item[0])){                map.put(item[0], new ArrayList<String>());            }            map.get(item[0]).add(item[1]);        }                for(ArrayList<String> al : map.values()) Collections.sort(al);                List<String> path = new ArrayList<String>(); path.add("JFK");        dfs(map, path, tickets.length + 1);        return path;    }        public boolean dfs(Map<String, ArrayList<String>> map, List<String> path, int tarLen){        if(path.size() == tarLen){            return true;        }                String cur = path.get(path.size() - 1);        ArrayList<String> dest= map.get(cur);                for(int i = 0; dest != null && i < dest.size(); i++){            String next = dest.get(i);             path.add(next);            dest.remove(i);            if(dfs(map, path, tarLen)) return true;            path.remove(path.size() - 1);            dest.add(i, next);        }        return false;    }}// [["JFK","KUL"],["JFK","NRT"],["NRT","JFK"]] 访问K,回溯,访问N J 又要访问K

网上还有更快的解法:

说是拓补排序。拓补排序一般两种,一种是入度为0的点用队列存,然后取点,修改其他点的入度,若为0入队;一种是出度为0,输出的顺序刚好相反。

这题其实是有环的,为啥使用拓补排序老实说没想通。什么欧拉回路。。不知所云。代码是这样的,的确快:

public class Solution{ // 8msHashMap<String, PriorityQueue<String>> map = new HashMap<String, PriorityQueue<String>>();LinkedList<String> result = new LinkedList<String>(); public List<String> findItinerary(String[][] tickets) {for (String[] ticket : tickets) {if (!map.containsKey(ticket[0])) {PriorityQueue<String> q = new PriorityQueue<String>();map.put(ticket[0], q);}map.get(ticket[0]).offer(ticket[1]);} dfs("JFK");return result;} public void dfs(String s) {PriorityQueue<String> q = map.get(s); while (q != null && !q.isEmpty()) {dfs(q.poll());} result.addFirst(s);}}

先遍历所有next机场,并删除,当前节点被addFirst当且仅当所有的next 机场都被访问过并删除。

[["JFK","KUL"],["JFK","NRT"],["NRT","JFK"]]

先JFK, 访问K和N,K没有next,[K],

然后访问N, N有J。访问J,访问N的时候N已经从J的NEXT删除了。这时候J没有next [J, K]

然后回到N,输出N [N J K]

然后回到J,输出J [J N J K]

0 0
原创粉丝点击