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:
- 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"]
. - All airports are represented by three capital letters (IATA code).
- 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.
Credits:
Special thanks to @dietpepsi for adding this problem and creating all test cases.
这道题的意思是说有一些飞机票,要从这些票里面恢复出行程,每个行程都是从“JFK”开始的,如果有多种答案,就按照字典序取最小的那个。
乍一看好像挺简单的,每张票做个hash,然后跑一遍就行。
不过存在一个case,比如
[["JFK","KUL"],["JFK“,"NRT"],["NRT","JFK"]]
如果按照字典序,JFK有两个目的地,KUL和NRT。搜索时会先搜索KUL,这样就没后路了,应该要先搜索NRT。对于这个问题,
http://bookshadow.com/weblog/2016/02/05/leetcode-reconstruct-itinerary/的博主的第一种答案给出了方法。设定两个[], left 跟 right,搜索路径的时候如果没有回到出发地的让它靠后,让有出发地的靠前,这样可以保证看起来是合理的行程,其中为了防止在删路径的时候仍然访问删除的路径,还判断了下路径是否还存在着。
import collections class Solution(object): def findItinerary(self, tickets): """ :type tickets: List[List[str]] :rtype: List[str] """ dest = collections.defaultdict(list) for t in tickets: dest[t[0]].append(t[1]) # for k, v in dest.iteritems(): # dest[k] = sorted(v) def dfs(start): left, right = [], [] for end in sorted(dest[start]): if end not in dest[start]: continue dest[start].remove(end) subroute = dfs(end) if start in subroute: left += subroute else: right += subroute return [start] + left + right return dfs("JFK")
其中 collections.defaultdict(list) 是内建了一个每次都能直接生成list的dict, 访问这个dict的时候如果没找到就直接生成一个list。
说回来这个问题应该是一个欧拉回路问题,即不重复的把图中所有边都走一遍。对于这个问题,有Hierholzer算法可以求解。
Hierholzer算法把每次访问的节点入栈,若节点无后续可访问的路径,则出栈,这样保持了每一个节点继续被访问的可能性。利用函数调用递归就是栈的特性,直接建立一个dfs函数,用它当栈,于是有:
class Solution(object): def findItinerary(self, tickets): """ :type tickets: List[List[str]] :rtype: List[str] """ dest = collections.defaultdict(list) for t in tickets: dest[t[0]].append(t[1]) for k,v in dest.iteritems(): dest[k]=sorted(v,reverse=True) route=[] def dfs(start): while(dest[start]): dfs(dest[start].pop()) route.append(start) dfs("JFK") return route[::-1]
最后返回这个出栈序列的反。
用c++实现如下:
#include<iostream> #include<set> #include<vector> #include<unordered_map> #include<algorithm> #include<string> using namespace std; class Solution { public: unordered_map<string,multiset<string>> dest; vector<string> route; void dfs(string start){ while(dest[start].size()>0){ auto pre= &dest[start]; string tmp = *(pre->begin()); pre->erase(pre->begin()); dfs(tmp); } route.push_back(start); } vector<string> findItinerary(vector<pair<string, string>> tickets){ for(auto e:tickets) dest[e.first].insert(e.second); dfs("JFK"); reverse(route.begin(),route.end()); return route; } };
其中利用multiset自带有序的特性,直接取出来。unordered_map比起map搜索时间更短,但耗费的空间更大。
总的来说,这道题还是挺有趣的,涉及到的一些知识点包括欧拉回路,Hierholzer算法,DFS等。