欧拉回路

624 阅读4分钟

简介

说到欧拉回路,相信大家都听说过柯尼斯堡七桥问题

图片来源网络

简单概括欧拉回路就是在一个双向图中,从一个节点开始经过图中的所有边,然后回到了起点,这个回路就是欧拉回路,这个从起点到起点的路径就是欧拉路径。同样如果从一个节点出发,经过图中所有的边,没有回到起点,就是欧拉通路

定义

欧拉通路:设G=<V,E>是一个没有孤立点的无向图,则通过G中每条边一次且仅一次的通路称为欧拉通路。

欧拉回路:设G=<V,E>是一个没有孤立点的无向图,则通过G中每条边一次且仅一次的回路称为欧拉回路。

ps:

  • 在欧拉通路或欧拉回路中边不能重复但顶点可以重复。
  • 由于在欧拉通路或欧拉回路中包含了图的所有边且图中没有孤立点,所以一定包含了图的所有顶点.因此,存在欧拉通路或欧拉回路的图一定是连通图。

欧拉图:具有欧拉回路的图称为欧拉图。

欧拉回路存在情况: 1.节点的度数全都是偶数。 2.只有节点的度数为奇数,那么一定从奇数点开始。 3.存在两个奇数节点,那么一定从一个奇数点开始一个奇数点结束。(是欧拉通路)

欧拉通路的判断方法:

  • 有向图:图连通,有一个顶点出度大于入度,有一个顶点入度大于出度,其余都是出度等于入度。
  • 无向图:图连通,只有两个顶点是奇数度,其余都是偶数度。

欧拉回路的判断方法:

  • 有向图:图连通,所有的顶点出度等于入度。
  • 无向图:图连通,所有的顶点都是偶数度。

例题

七桥问题

哥尼斯堡七桥问题(以下简称七桥问题)。17世纪的东普鲁士有一座哥尼斯堡城(现在叫加里宁格勒,在波罗的海南岸),城中有一座岛,普雷格尔河的两条支流环绕其旁,并将整个城市分成北区、东区、南区和岛区4个区域,全城共有七座桥将4个城区连接起来。于是,产生了一个有趣的问题:一个人是否能在一次步行中经过全部的七座桥后再回到起点,且每座桥只经过一次。

我们可以把问题抽象:

image.png

将城区抽象为顶点,用A、B、C、D表示4个城区,将桥抽象为边,用7条边表示七座桥,抽象出七桥问题的数据模型如图(b)所示,从而将七桥问题抽象为一个数学问题:求经过图中每条边一次且仅一次的回路,后来人们称之为欧拉回路。欧拉回路的判定规则是:

  1. 如果通奇数个桥的城区多于两个,则不存在欧拉回路。
  2. 如果只有两个城区通奇数个桥,则可以从这两个城区之一出发找到欧拉回路。
  3. 如果没有一个城区通奇数个桥,则无论从哪里出发都能找到欧拉回路。

由上述判定规则得到求解七桥问题的基本思路:依次计算与每个顶点相关联的边的个数(称为顶点的度),根据度为奇数的顶点个数判定是否存在欧拉回路。

Hierholzer 算法

Hierholzer 算法是一种用于求欧拉路径的算法。

算法过程如下:

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

伪代码:

算法:EulerCircuit

输入:二维数组mat[n][n]

输出:度为奇数的顶点个数count。

1.count初始化为0;

2.下标i从0~n-1重复执行下述操作:

2.1 计算第i行元素之和degree;

2.2 如果degree为奇数,则count++;

3.返回count;

332. 重新安排行程

class Solution {
public:
    unordered_map<string,priority_queue<string, vector<string>, std::greater<string>>> vec;
    vector<string> answer;
    void dfs(string target){
        while(vec.count(target)&& vec[target].size() > 0){
            string str = vec[target].top();
            vec[target].pop();
            dfs(str);
        }
        answer.push_back(target);
    }
    vector<string> findItinerary(vector<vector<string>>& tickets) {
        for(auto ticket: tickets){
            vec[ticket[0]].emplace(ticket[1]);
        }
        dfs("JFK");
        reverse(answer.begin(),answer.end());
        return answer;
    }
};

753. 破解保险箱

class Solution {
public:
    set<int> visted;
    string answer;
    int mod;
    void dfs(int num,int N,int K){
        int mod = pow(10,N - 1);
        for(int i = 0;i < K;i++){
            int node_val = num*10 + i;
            if(!visted.count(node_val)){
                visted.insert(node_val);
                dfs(node_val % mod,N,K);
                answer += (i + '0');
            }
        }
    }
    string crackSafe(int N, int K) {
        dfs(0,N,K);
        answer += string(N - 1, '0'); 
        return answer;
    }
};

参考

[1] 《数据结构与算法:C语言版》;胡明,王红梅