mirror of
https://github.com/ShusenTang/LeetCode.git
synced 2024-09-02 14:20:01 +00:00
4.7 KiB
4.7 KiB
332. Reconstruct Itinerary
思路
这道题给我们一堆飞机票,让我们安排行程路线,必须用完所有机票,如果有多种方法,取其中字母顺序小的那种方法。
思路一、Brute Force
这道题目可以转化成一个图来描述。图中每一个点就是一个地点,每张票代表的就是一个有向的边(需要注意的是图中可以存在环)。题目转换成要求一条搜索路径,要求走完所有边且每条边只能走一次。
所以最直接的想法就是直接普通的DFS,尝试所有可能的路径,具体步骤如下:
- 把输入转换成为图的形式,要求给定一个地点,就能快速地知道它所连接的邻居点们,并且可以lexical order的顺序的访问它们,我们可以用数据结构
unordered_map<string, vector<string>>
来表示这个图。 - 在构造的图上从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)
}
上述过程的图示举例可参考此博文。由于每条边访问后就被删掉了,所以时间复杂度为O(E),E为边数。
这道题目运用经典的hierholzer算法需要注意的是,由于我们需要优先访问 lexical order 比较小的点,所以我们用multiset存放每个节点的所有邻居,这样可以维持有序。
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;
}
};
思路二
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;
}
};