[leetcode]332. Reconstruct Itinerary

Posted on 2018-01-12 20:32  你猜我猜不猜你猜不猜  阅读(209)  评论(0)    收藏  举报

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.

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等。

博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3