欧拉回路/欧拉路径求解,Fleury算法,Hierhoizer算法

随声附和 提交于 2020-03-02 00:50:00

欧拉路径就是一笔画问题,在一个连通图中,一笔走完所有路径不能有重复路径。

无向图:
具有欧拉回路的 充要条件 图连通,所有点的度数为偶数
具有欧拉路径的 充要条件 图连通,具有0个或者2个奇数度数的结点

有向图:
具有欧拉回路的 充要条件 图连通,所有点的入度等于出度
具有欧拉路径的 充要条件 图连通,一个点的出度大入度1度,一个点的入度大于出度1度,其余点出度等于入度。

Fleury算法跟Hierhoizer算法很相似,但是Hierhoizer算法更快一点,遍历一遍图就可以了。

我感觉核心思想就是圈套圈,有欧拉回路的图可以抽象为很多圈套在一起,试想一下给你很多圈相切的图形,你该如何一笔画?当然是遇到切点就进入切点的园中继续走遇到切点就走。

Hierhoizer算法的思想就是如此。寻找欧拉回路的时候正确的起点出发,遍历该节下一个结点,然后删除此边重复此步骤,在一个存在欧拉路径的路中会找到一个环,然后用一个stack存储路径,从递归深处存储,遇到还有为访问完路径的结点就继续访问,这等价于全是园的图中遇到切点,我们就进入切点继续访问。

看代码辅助理解一下:

vector<int> G[10000]; //邻接表存图
int nums[1000][1000]; //存边的条数
stack<int> path;
void dfs(int s) { //起点
        for(auto v:G[s]) { //遍历该结点的所有路径
            if(nums[s][v] >= 1) { //有路
                nums[s][v]--; //删除路
                dfs(v); //遍历下一个结点
            }
        }
        path.push(s); //此结点没有路径可走加入路径中
    }

最后输出path就可以了,stack存的是反路径,从顶取出就是正确路径了。
我们用dfs的特点来模仿遇到切点就进入,因为dfs只有平行状态执行完毕才算这层递归执行完毕。

Fleury算法思想很类似,不过它这个算法是这么说的:
访问一条边就删除这条边。
优先访问不是桥的边。

在这里插入图片描述

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<vector>
using namespace std;

const int N = 1005;
int n, m, flag, top, sum, du[N], ans[5005], map[N][N];

void dfs(int x)
{
    ans[++top] = x;
    for(int i = 1; i <= n; i++)
    {
        if(map[x][i] >= 1)
        {
            map[x][i]--;
            map[i][x]--;
            dfs(i);
            break;
        }
    }
}

void fleury(int x)
{
    top = 1;
    ans[top] = x;
    while(top > 0)
    {
        int k = 0;
        for(int i = 1; i <= n; i++)//判断是否可扩展
        {
            if(map[ans[top]][i] >= 1)//若存在一条从ans[top]出发的边  那么就是可扩展
            {k = 1; break;}
        }
        if(k == 0)//该点x没有其他的边可以先走了(即不可扩展), 那么就输出它
        {
            printf("%d ", ans[top]);
            top--;
        }
        else if(k == 1)//如可扩展, 则dfs可扩展的哪条路线
        {
            top--;//这需要注意
            dfs(ans[top+1]);
        }
    }
}
int main()
{
    while(scanf("%d%d", &n, &m) != EOF)
    {
        memset(du, 0, sizeof(du));
        memset(map, 0, sizeof(map));

        for(int i = 1; i <= m; i++)
        {
            int x, y;
            scanf("%d%d", &x, &y);
            map[x][y]++; //记录边, 因为是无向图所以加两条边, 两个点之间可能有多条边
            map[y][x]++;
            du[x]++;
            du[y]++;
        }
        flag = 1; // flag标记开始点。 如果所有点度数全为偶数那就从1开始搜
        sum = 0;
        for(int i = 1; i <= n; i++)
        {
            if(du[i] % 2 == 1)
            {
                sum++;
                flag = i;// 若有奇数边, 从奇数边开始搜
            }
        }
        if(sum == 0 || sum == 2)
            fleury(flag);
    }
    return 0;
}

这个算法类似与把Hierhoizer给细分了,一个dfs专门找回路,然后把访问完周围结点的点加入答案,遇到还可以访问其他结点的点就继续dfs,也就是我所说的进入切点。他给拆开了,更好理解,也可看出时间复杂度高于Hierhoizer算法每个出栈的点都要判断一下是不是可以扩展路径。我并没从此代码看出不选桥的特点,可能是我看不出来吧。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!