Leetcode 332 重新安排行程(欧拉路径 Hierholzer算法)
一些预备知识:
- 欧拉路径:
如果在一张图中,可以从一点出发遍历所有的边,那么遍历过程中的这条路径就叫做欧拉路径。如果这条路径是闭合的,那就称为欧拉回路。
简单地说,如果一张图可以被“一笔画”,那么“一笔画”的那个轨迹就叫做欧拉路径。
- 欧拉图判定定理:
含有欧拉回路的图称为欧拉图,含有欧拉路径的图称为半欧拉图。
无向图中,如果所有顶点的度数都为偶数,则为欧拉图;如果有两个顶点的度数为奇数,其他的为偶数,则为半欧拉图。
有向图中,如果所有顶点的入度等于出度,那么就是欧拉图;如果有两个顶点的入度不等于出度,其他的节点入度等于初读,则为半欧拉图。如果在一张图中,可以从一点出发遍历所有的边,那么遍历过程中的这条路径就叫做欧拉路径。如果这条路径是闭合的,那就称为欧拉回路。
简单地说,如果一张图可以被“一笔画”,那么“一笔画”的那个轨迹就叫做欧拉路径。 - Hierholzer算法:
解决的问题:给出一个有向图,且为欧拉图,求欧拉回路。
算法流程:
- 选择任一顶点为起点,遍历所有相邻边。
- 深度搜索,访问相邻顶点。将经过的边都删除(标记一下就可以了)。
- 如果当前顶点没有相邻边,则将顶点入栈。
- 栈中的顶点倒序输出,就是从起点出发的欧拉回路。
性质一:如果该图为欧拉图,则栈底的必定为起点。如果该图为半欧拉图,则栈底部存储的是与起点不同的另外一个奇度数顶点。
性质二:如果该图为欧拉图(/半欧拉图),则栈中的自底到顶第 个顶点就是欧拉回路(/欧拉路径)上的第 个顶点。
题目描述:
给定一个机票的字符串二维数组 [from, to],子数组中的两个成员分别表示飞机出发和降落的机场地点,对该行程进行重新规划排序。所有这些机票都属于一个从JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 出发。有以下三个要求:
- 如果存在多种有效的行程,你可以按字符自然排序返回最小的行程组合。例如,行程 ["JFK", "LGA"] 与 ["JFK", "LGB"] 相比就更小,排序更靠前
- 所有的机场都用三个大写字母表示(机场代码)。
- 假定所有机票至少存在一种合理的行程。
根据上述要求梳理出一个字典序较小的欧拉路径
题解:
要注意的这题是有重边的,因此边的标记就不能用普通的01标记了。比较优雅的写法是用unordered_map嵌套一个map来构建邻接矩阵。map内部是有序的,这样在遍历的时候自然会优先遍历到字典序小的车站。之后套一下Hierholzer求出欧拉路径就好了。
AC代码:
贴两个吧,第一个是借鉴了别人的代码,优雅了很多(嵌套map,一些遍历之类的)
第二个是自己手搓的,没有灵活的用好map的嵌套,还人为的排序了,多费了不少。
class Solution { public: // typedef map内部为升序 typedef unordered_map<string,map<string,int>> adjacent; vector<string> ans; void euler(adjacent& adj,string now) { // map 遍历 加上 & 的化 对conut的修改 会直接影响到 adj[now][next] for(auto & [next,count] : adj[now]) { if(count >= 1) { // adj[now][next]--; count--; euler(adj,next); } } ans.push_back(now); } vector<string> findItinerary(vector<vector<string>>& tickets) { // 熟悉一下迭代的遍历方式 摒弃之前的习惯 // map unorder_map灵活运用 // 邻接矩 邻接表的使用 // build_map adjacent adj; // 优美 for(auto & t:tickets) { adj[t[0]][t[1]]++; } ans.clear(); euler(adj,"JFK"); reverse(ans.begin(),ans.end()); return ans; } };
class Solution { public: //TOPO // str -> int // int -> str int string_int(string s) { if(mp_str.find(s) != mp_str.end()) return mp_str[s]; int id = mp_str.size(); mp_str[s] = id; mp_int[id] = s; return id; } void euler(int pos) { int Len = edge[pos].size(); sort(edge[pos].begin(),edge[pos].end()); for(int i=0;i<Len;i++) { string next = edge[pos][i]; if(vis[pos][mp_str[next]] >=1) { vis[pos][mp_str[next]]--; euler(mp_str[next]); } } ans.push_back(mp_int[pos]); } vector<string> findItinerary(vector<vector<string>>& tickets) { int Len = tickets.size(); // build_map memset(vis,sizeof(vis),0); for(int i=0;i<Len;i++) { int from = string_int(tickets[i][0]); int to = string_int(tickets[i][1]); edge[from].push_back(tickets[i][1]); vis[from][to]++; } int city_counts = mp_str.size(); ans.clear(); euler(mp_str["JFK"]); reverse(ans.begin(),ans.end()); return ans; } private: map<string,int> mp_str; map<int,string> mp_int; vector<string> edge[10010]; vector<string> ans; int vis[1010][1010]; };
一些想法:
1. 写好一题之后如果时间or空间复杂度的排名不咋地,要去看看别人的写法。
2. 数据结构要灵活使用,别死脑筋的一直用之前的套路,想一想用哪些stl的工具可以提高效率。