# [332. Reconstruct Itinerary](https://leetcode.com/problems/reconstruct-itinerary/) # 思路 这道题给我们一堆飞机票,让我们安排行程路线,必须用完所有机票,如果有多种方法,取其中字母顺序小的那种方法。 ## 思路一、Brute Force 这道题目可以转化成一个图来描述。图中每一个点就是一个地点,每张票代表的就是一个有向的边(需要注意的是图中可以存在环)。题目转换成要求一条搜索路径,要求走完所有边且每条边只能走一次。 所以最直接的想法就是直接普通的DFS,尝试所有可能的路径,具体步骤如下: 1. 把输入转换成为图的形式,要求给定一个地点,就能快速地知道它所连接的邻居点们,并且可以lexical order的顺序的访问它们,我们可以用数据结构`unordered_map>`来表示这个图。 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>&G, string from, unordered_map>&visited, vector &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 findItinerary(vector>& tickets) { unordered_map>G; unordered_map>visited; setcity; 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(G[a].size(), 0); } vectorres; DFS(G, "JFK", visited, res); return res; } }; ``` ## 思路二 ``` C++ class Solution { private: unordered_map>G; vectorres; // 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 findItinerary(vector>& tickets) { for(auto &t: tickets) G[t[0]].insert(t[1]); DFS("JFK"); reverse(res.begin(), res.end()); return res; } }; ```