LeetCode/solutions/332. Reconstruct Itinerary.md
2019-12-06 22:26:29 +08:00

137 lines
4.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# [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;
}
};
```