欧拉路径 LeetCode 332

欧拉路径是图中这样一条路径,包含了所有边,且只包含一次。如果起点和终点是同一个点,则是一种特殊的欧拉路径——欧拉环路。


求法1:暴力法:遍历所有的路径,如果当前路径长度为总边数,则当前路径是一条欧拉路径,如果一直没有碰到这样的路径,则无解。

一个图总共多少条路径(一般是有环的)

1)不允许路径里有环,也就是路径里的点都只出现一次的,|V|!(第一个点|V|种选择,第二个|V| - 1种。。 n * (n - 1) ...

2)  路径里每条边只能用一次,但是顶点可以出现多次。|E|!


欧拉路径是第2种路径。

区别就是判重的 inPath用的是点还是边的问题

总结一下:

1)普通的 顶点遍历 mark vertex 后不会unmark, O(|V|)

2)路径遍历, inPath mark X,退出后unmark  X, O(|X|!), 其中,对于基于点的路径 X= V,基于边的路径 X= E,


第二种遍历和全排列类似,其实搜索的已经不是原来的graph,状态定义不是vertex,而是路径,是一个新的隐式图,和全排列的隐式图一样,是一棵树。


class Solution {
public:
    vector<string> findItinerary(vector<pair<string, string>> tickets) {
        unordered_map<string, unordered_map<string, int>> G;
        for (auto &ticket : tickets) {
            G[ticket.first][ticket.second] += 1;
        }
        vector<string> path;
        vector<vector<string>> ans;
        function<void(string, int)> dfs = [&](string v, int n) {
            if (n == tickets.size()) {
                ans.push_back(path);
            }
            else {
                for (auto &e : G[v]) {
                    if (e.second > 0) {
                        e.second -= 1;
                        path.push_back(e.first);
                        dfs(e.first, n + 1);
                        e.second += 1;
                        path.pop_back();
                    }
                }
            }
        };
        path.push_back("JFK");
        dfs("JFK", 0);
        sort(ans.begin(), ans.end());
        return ans[0];
    }
    
};


方法2:Hierholzer's algorithm 有点象拓扑排序的“基于边”的版本

前提是先确定确实存在欧拉路径,根据顶点的度数。

存在欧拉回路和欧拉路径的判断:
1)欧拉回路:
无向图:每个顶点的度数都是偶数,则存在欧拉回路。
有向图:每个顶点的入度都等于出度,则存在欧拉回路。
2)欧拉路径:
无向图:当且仅当该图所有顶点的度数为偶数 或者 除了两个度数为奇数外其余的全是偶数。
有向图:当且仅当该图所有顶点 出度=入度 或者 一个顶点 出度=入度+1,另一个顶点 入度=出度+1,其 他顶点 出度=入度。


然后用Hierholzer算法:

class Solution {
public:
    vector<string> findItinerary(vector<pair<string, string>> tickets) {
        unordered_map<string, priority_queue<string, vector<string>, greater<string>>> G;
        for (auto &ticket : tickets) {
            G[ticket.first].push(ticket.second);
        }
        
        vector<string> path;
        function<void(string)> dfs = [&](string v) {
                auto & edges = G[v];
                while (edges.size() > 0) {
                    string to = edges.top(); edges.pop();//这里把边从邻接表里删去就是相当于mark这条边。
                    dfs(to);
                }
                path.push_back(v);
        };
        
        dfs("JFK");
        reverse(path.begin(), path.end());
        return path;
    }
    
};




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值