算法之----欧拉回路,欧拉通路,半欧拉图

若一个图为欧拉图或半欧拉图都可以通过一笔画遍历。

通过图(有向图或无向图)中的所有边且每一条边仅通过一次的通路称为欧拉通路,若此通路为回路则称为欧拉回路。

具有欧拉回路的图称为欧拉图,具有欧拉通路而无欧拉回路的图称为半欧拉图

无向图:

无向图G是欧拉图:当且仅当G是连通的且没有奇度顶点。

无向图G是半欧拉图:当且仅当G是连通的且恰有两个奇度顶点。



若图G中存在这样一条路径,使得它恰通过G中每条边一次,则称该路径为欧拉路径。若该路径是一个圈,则称为欧拉(Euler)回路

(用于连通判断)DFS(Depth-First-Search) 深度优先 搜索算法,是搜索算法的一种。下图是一个无向图,如果我们从A点发起深度优先搜索(以下的访问次序并不是唯一的,第二个点既可以是B也可以是C,D),则我们可能得到如下的一个访问过程:A->B->E(没有路了!回溯到B)->C->F->H->G->D(没有路,最终回溯到 A,A也没有未访问的相邻节点,本次搜索结束).
B--E
/
A-C--F
\ >H
D--G
特点“:  
       每次深度优先搜索的结果必然是图的一个连通分量。深度优先搜索可以从多点发起。如果将每个节点在深度优先搜索过程中的“结束时间”排序(具体做法是创建一个list,然后在每个节点的相邻节点都已被访问的情况下,将该节点加入list结尾,然后逆转整个链表),则我们可以得到所谓的“拓扑排序”,即topological sort.
搜索算法毕竟是 时间复杂度是O(n!)的阶乘级算法,它的效率比较低,在数据规模变大时,这种算法就显得力不从心了。对于很多问题,可以把搜索与动态规划(DP,dynamic programming)、完备匹配( 匈牙利算法)等高效算法结合。
【无向图】
一个无向图是欧拉图当且仅当该图是连通的(注意,不考虑图中度为0的点,因为它们的存在对于图中是否存在欧拉环、欧拉路径没有影响)且所有点的度数都是偶数;一个无向图是半欧拉图当且仅当该图是连通的且有且只有2个点的度数是奇数(此时这两个点只能作为欧拉路径的起点和终点);

证明:因为任意一个点,欧拉环(或欧拉路径)从它这里进去多少次就要出来多少次,故(进去的次数+出来的次数)为偶数,又因为(进去的次数+出来的次数)=该点的度数(根据定义),所以该点的度数为偶数。

【有向图】
一个有向图是欧拉图当且仅当该图的基图(将所有有向边变为无向边后形成的无向图,这里同样不考虑度数为0的点)是连通的且所有点的入度等于出度;一个有向图是半欧拉图当且仅当该图的基图是连通的且有且只有一个点的入度比出度少1(作为欧拉路径的起点),有且只有一个点的入度比出度多1(作为终点),其余点的入度等于出度。

1    欧拉回路存在性的判断

欧拉回路问题可以分为无向图中的欧拉回路和欧拉通路,有向图中的欧拉回路和欧拉通路。这几个问题大抵相像。

有向欧拉回路有:

定理:假设有像多重图D有性质:当忽略有向边上的方向时,得到的图是连通的,那么D有有向欧拉回路当且仅当D的每个顶点的入度和出度相等。

类似的,对有向欧拉通路有:

定理:D有有向欧拉通路,当且仅当除两个不同顶点B和C之外,D的其它顶点的入度和出度相等,且B的出度比入度大1,C的入度比出度大1。在这种情况下,有向欧拉通路自B出发,至C终止。

由上面的定理可以知道,如果要判断一个有向图的欧拉回路是否存在,需要先判断连通性,再判断出度入度。对于无向图,判断方法类似。

判断连通性可以通过DFS或者并查集来实现。

欧拉定理   如果一个网络是连通的并且奇顶点的个数等于0或2,那么它可以一笔画出;否则它不可以一笔画出。

判断一笔画的方法:

  ①是连通的。一个图,如果图上任意二点总有线段连接着,就称为连通的。不是连通的就不能一笔画出。

  ②奇点个数是0或者是2。图上线段的端点可以分成二类,奇点和偶数。一个点,以它为端点的线段数是奇数就称为奇点,线段数是偶数就称为偶点。

  一个图是否是一笔画就看奇点的个数,奇点个数是 0 或者 2,就是一笔画,否则就不是一笔画。

所以这个问题完全可以转化策略为:

           第一步: 首先我们不管它三七二十几,先进行连通性的判断。

           第二步:

                      (1)如果是连通的,我们来判断此图的度的奇点的个数是0或者是2 ,如果是,则说明这个是欧拉图,即可以一笔画出,反之则不能一笔画出

                      (2)如果是非连通的,这说明这个图很定不能一笔画出。

 

#include <cstdio>
#include <cstring>
using namespace std;

int mp[1010][1010];
int n,p,q,a,b;
short vis[1010],c[1010];

void dfs(int cur)
{
    vis[cur]=1;
    for(int i=1;i<=p;i++)
    {
        if(mp[cur][i]&&!vis[i])
        {
            dfs(i);
        }
    }
}
        
int main()
{
    scanf("%d",&n);
    while(n--)
    {
        memset(mp,0,sizeof(mp));
        memset(vis,0,sizeof(vis));
        memset(c,0,sizeof(c));
        scanf("%d%d",&p,&q);
        for(int i=0;i<q;i++)
        {
            scanf("%d%d",&a,&b);
            mp[a][b]=mp[b][a]=1;
        }
        int cn=0;
        for(int i=1;i<=p;i++)
        {
            for(int j=1;j<=p;j++)
                c[i]+=mp[i][j];
            if(c[i]%2==1)
                cn++;
        }
        dfs(1);
        int ok=1;
        for(int i=1;i<=p;i++)
            if(!vis[i])
        {
            ok=0;
            break;
        }
        if(ok&&(cn==0||cn==2))
        {
            printf("Yes\n");
        }
        else
            printf("No\n");
    }
    return 0;
}

Fleury(佛罗莱)算法求欧拉回路的学习

#include<iostream>
#include<stack>
const int MAXN=111;
using namespace std;

stack<int>S;
int edge[MAXN][MAXN];
int n,m;

void dfs(int x){
    S.push(x);
    for(int i=1;i<=n;i++){
        if(edge[x][i]>0){
            edge[i][x]=edge[x][i]=0;//删除此边
            dfs(i);
            break;
        }
    }
}

//Fleury算法的实现
void Fleury(int x){
    S.push(x);
    while(!S.empty()){
        int b=0;
        for(int i=1;i<=n;i++){
            if(edge[S.top()][i]>0){
                b=1;
                break;
            }
        }
        if(b==0){
            printf("%d",S.top());
            S.pop();
        }else {
            int y=S.top();
            S.pop();
            dfs(y);//如果有,就dfs
        }
    }
    printf("\n");
}

int main(){
    scanf("%d%d",&n,&m); //读入顶点数以及边数
    memset(edge,0,sizeof(edge));
    int x,y;
    for(int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        edge[x][y]=edge[y][x]=1;
    }
    //如果存在奇数顶点,则从奇数顶点出发,否则从顶点0出发
    int num=0,start=1;
    for(int i=1;i<=n;i++){                        //判断是否存在欧拉回路
        int degree=0;
        for(int j=1;j<=n;j++){
            degree+=edge[i][j];
        }
        if(degree&1){
            start=i,num++;
        }
    }
    if(num==0||num==2){
        Fleury(start);
    }else
        printf("No Euler Path\n");
    return 0;
}


  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值