给定一个机票的字符串二维数组 [from, to]
,子数组中的两个成员分别表示飞机出发和降落的机场地点,对该行程进行重新规划排序。所有这些机票都属于一个从JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 出发。
说明:
- 如果存在多种有效的行程,你可以按字符自然排序返回最小的行程组合。例如,行程 ["JFK", "LGA"] 与 ["JFK", "LGB"] 相比就更小,排序更靠前
- 所有的机场都用三个大写字母表示(机场代码)。
- 假定所有机票至少存在一种合理的行程。
示例 1:
输入: [["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]]
输出: ["JFK", "MUC", "LHR", "SFO", "SJC"]
示例 2:
输入:[["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]]
输出: ["JFK","ATL","JFK","SFO","ATL","SFO"]
解释: 另一种有效的行程是 ["JFK","SFO","ATL","JFK","ATL","SFO"]。但是它自然排序更大更靠后。
思路:这个题目实际上是欧拉路径问题(一笔画问题):
概念科普:
若一个图为欧拉图或半欧拉图都可以通过一笔画遍历。
通过图(有向图或无向图)中的所有边且每一条边仅通过一次的通路称为欧拉通路,若此通路为回路则称为欧拉回路。
具有欧拉回路的图称为欧拉图,具有欧拉通路而无欧拉回路的图称为半欧拉图。
dfs来解决:
1、采用邻接表来表示有向图,但是要注意两个机场间可能有多张机票,所有还要多一个计数。
抽象成一个map<string, map<string, int> > &m,里面存每一个顶点的后继顶点,int是机票数。
2、由于题目要求按照字母顺序排序,所以用map替代unordered_map, unordered_map是哈希表,map是红黑树,输出有序的。
unordered_map查找时间复杂度O(1),map查找时间复杂度O(logn)。这里牺牲查找时间换输出有序功能。
3、设置标记数组,如果该行程没有机票剩余,直接跳过。
4、如果已经找到第一个字典序结果,直接返回,避免继续for循环,re被后面的字典序较大的结果覆盖。
class Solution {
public:
vector<string>re;
void dfs(vector<string> &temp, map<string, map<string, int> > &m, map<string, map<string, int> > &v, int n){
if(temp.size()==n){
re=temp;
return;
}
for(auto x: m[temp.back()]){
string stop=x.first;//当前行程的终点站
//说明所有机票用完了
if(v[temp.back()][stop]==x.second) continue;
v[temp.back()][stop]++;
temp.push_back(stop);
dfs(temp, m , v, n);
temp.pop_back();
v[temp.back()][stop]--;
//如果已经找到一个结果,直接返回,避免for循环到最后,re被后面的字典序较大的结果覆盖
if(re.size()==n) return;
}
}
vector<string> findItinerary(vector<vector<string>>& tickets) {
//两个机场间可能有多张机票,所以value用int来计数
//map保证value有序,可以保证第一个搜索结果即为最终结果。unordered_map是哈希表,map是红黑树,输出有序的
map<string, map<string, int> >m, v;//v是标记数组
for(auto x: tickets){//初始化m
m[x[0]][x[1]]++;
}
vector<string>temp;
temp.push_back("JFK");
dfs(temp, m, v, tickets.size()+1);
return re;
}
};