# [332. Reconstruct Itinerary](https://leetcode.com/problems/reconstruct-itinerary/)

# 思路
这道题给我们一堆飞机票,让我们安排行程路线,必须用完所有机票,如果有多种方法,取其中字母顺序小的那种方法。


## 思路一、Brute Force

这道题目可以转化成一个图来描述。图中每一个点就是一个地点,每张票代表的就是一个有向的边(需要注意的是图中可以存在环)。题目转换成要求一条搜索路径,要求走完所有边且每条边只能走一次。

所以最直接的想法就是直接普通的DFS,尝试所有可能的路径,具体步骤如下:
1. 把输入转换成为图的形式,要求给定一个地点,就能快速地知道它所连接的邻居点们,并且可以lexical order的顺序的访问它们,我们可以用数据结构`unordered_map<string, vector<string>>`来表示这个图。
2. 在构造的图上从JFK开始做DFS,访问所有能走的路,同时记录已访问过的边,一旦边数达到票数加1,说明已经用完所有票,直接return,否则应该回溯。


## 思路二、欧拉路径(Hierholzer算法)

题目要求的这个路径其实叫做欧拉路径,欧拉路径是指一条包含图中所有边的一条路径,该路径中所有的边会且仅会出现一次。

```
一个无向图中存在欧拉路径,当且仅当下面两条性质同时满足:
* 图是连通的
* 图中每个顶点的度均为偶数

而一个有向图存在欧拉路径,当且仅当下面两条性质同时满足:
* 图是连通的
* 图中每个顶点入度和出度相同
```

那么,这个题目就可以转化成:已知有向图中存在欧拉路径,如何找到一个欧拉路径?

Hierholzer算法是求欧拉路径的一个经典算法,它其实也是DFS,其伪代码如下:
```
// path逆序记录着欧拉路径
DFS(u){
    While u存在未被访问的边e(u,v):
        记录边e(u,v)为访问
        DFS(v)
    path.push(u)
}
```
由于上述过程不用回溯,所以我们不用visited数组来记录某条边已被访问过而是直接将改边删除即可,即
```
// path逆序记录着欧拉路径
DFS(u){
    While u存在边e(u,v):
        删除边e(u,v)
        DFS(v)
    path.push(u)
}
```

上述过程的图示举例可参考[此博文](https://www.geeksforgeeks.org/hierholzers-algorithm-directed-graph/)。由于每条边访问后就被删掉了,所以时间复杂度为O(E),E为边数。


这道题目运用经典的hierholzer算法需要注意的是,由于我们需要优先访问 lexical order 比较小的点,所以我们用multiset存放每个节点的所有邻居,这样可以维持有序。





# C++
## 思路一
``` C++
class Solution {
private:
    int tickets_num;
    void DFS(unordered_map<string, vector<string>>&G, string from, 
             unordered_map<string, vector<int>>&visited,
             vector<string> &res){
        res.push_back(from);
        if(res.size() == tickets_num + 1) return;
        
        int size = G[from].size();
        for(int i = 0; i < size; i++){
            string next = G[from][i];
            if(visited[from][i]) continue;

            visited[from][i] = 1;
            DFS(G, next, visited, res);
            if(res.size() == tickets_num + 1) return;
            // 回溯
            visited[from][i] = 0;
            res.pop_back();
        }
    }
public:
    vector<string> findItinerary(vector<vector<string>>& tickets) {
        unordered_map<string, vector<string>>G;
        unordered_map<string, vector<int>>visited;
        set<string>city;
        tickets_num = tickets.size();
        for(int i = 0; i < tickets.size(); i++){
            G[tickets[i][0]].push_back(tickets[i][1]);
            city.insert(tickets[i][0]);
            city.insert(tickets[i][1]);
        }
            
        for(auto &a: city){
            sort(G[a].begin(), G[a].end()); // 按照字符顺序排序
            visited[a] = vector<int>(G[a].size(), 0);
        }
        
        vector<string>res;
        DFS(G, "JFK", visited, res);
        return res;
    }
};
```

## 思路二
``` C++
class Solution {
private:
    unordered_map<string, multiset<string>>G;
    vector<string>res;
    // Hierholzer算法
    void DFS(string from){
        while(!G[from].empty()){
            auto next_ptr = G[from].begin();
            string next = *next_ptr;
            G[from].erase(next_ptr);
            DFS(next);
        }
        res.push_back(from);
    }
public:
    vector<string> findItinerary(vector<vector<string>>& tickets) {
        for(auto &t: tickets)
            G[t[0]].insert(t[1]);
        
        DFS("JFK");
        reverse(res.begin(), res.end());
        return res;
    }
};
```