Leetcode 332 重新安排行程(欧拉路径 Hierholzer算法)

一些预备知识:

  • 欧拉路径

    如果在一张图中,可以从一点出发遍历所有的边,那么遍历过程中的这条路径就叫做欧拉路径。如果这条路径是闭合的,那就称为欧拉回路
    简单地说,如果一张图可以被“一笔画”,那么“一笔画”的那个轨迹就叫做欧拉路径

  • 欧拉图判定定理:

    含有欧拉回路的图称为欧拉图,含有欧拉路径的图称为半欧拉图。
    无向图中,如果所有顶点的度数都为偶数,则为欧拉图;如果有两个顶点的度数为奇数,其他的为偶数,则为半欧拉图。
    有向图中,如果所有顶点的入度等于出度,那么就是欧拉图;如果有两个顶点的入度不等于出度,其他的节点入度等于初读,则为半欧拉图。

    如果在一张图中,可以从一点出发遍历所有的边,那么遍历过程中的这条路径就叫做欧拉路径。如果这条路径是闭合的,那就称为欧拉回路
    简单地说,如果一张图可以被“一笔画”,那么“一笔画”的那个轨迹就叫做欧拉路径。

  • Hierholzer算法:

    解决的问题:给出一个有向图,且为欧拉图,求欧拉回路。

    算法流程:

    1. 选择任一顶点为起点,遍历所有相邻边。
    2. 深度搜索,访问相邻顶点。将经过的边都删除(标记一下就可以了)。
    3. 如果当前顶点没有相邻边,则将顶点入栈。
    4. 栈中的顶点倒序输出,就是从起点出发的欧拉回路。

     性质一:如果该图为欧拉图,则栈底的必定为起点。如果该图为半欧拉图,则栈底部存储的是与起点不同的另外一个奇度数顶点

   性质二:如果该图为欧拉图(/半欧拉图),则栈中的自底到顶第 [公式] 个顶点就是欧拉回路(/欧拉路径)上的第 [公式] 个顶点。

题目描述:

  给定一个机票的字符串二维数组 [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的工具可以提高效率。

原文地址:https://www.cnblogs.com/z1141000271/p/12628489.html