欧拉图总结

版权声明:本文为博主原创文章,转载请标明原博客。 https://blog.csdn.net/sdau_fangshifeng/article/details/83443325

---------------------------------------------------------------------------------------------
转自:https://blog.csdn.net/u013480600/article/details/44805491 

---------------------------------------------------------------------------------------------

                                               欧拉图详解


        通过图(无向图或有向图)中所有边一次且仅一次行遍图中所有顶点的通路称为欧拉通路,通过图中所有边一次且仅一次行遍所有顶点的回路称为欧拉回路。具有欧拉回路的图称为欧拉图(Euler Graph),具有欧拉通路而无欧拉回路的图称为半欧拉图。


        1.定义


        欧拉通路(Euler tour)——通过图中每条边一次且仅一次,并且过每一顶点的通路。
        欧拉回路 (Eulercircuit)——通过图中每条边一次且仅一次,并且过每一顶点的回路。


        2.无向图是否具有欧拉通路或回路的判定


        G有欧拉通路的充分必要条件为:G 连通,G中只有两个奇度顶点(它们分别是欧拉通路的两个端点)。
        G有欧拉回路(G为欧拉图):G连通,G中均为偶度顶点。


        3.有向图是否具有欧拉通路或回路的判定


        D有欧拉通路:D连通,除两个顶点外,其余顶点的入度均等于出度,这两个特殊的顶点中,一个顶点的入度比出度大1,另一个顶点的入度比出度小1。
        D有欧拉回路(D为欧拉图):D连通,D中所有顶点的入度等于出度。


        (注意:这里说有向图连通,说的是有向图是弱连通图。即把有向图中的边变成无向边,只要该图连通,那么原有向图即是弱连通图。实际中可用并查集判断是否弱连通)


         注意下面打印节点的代码,其实打印欧拉路径的节点轨迹可以先打印每条欧拉路上的边,然后取每条边的头结点即可(最后一条边还需要取尾部结点)。


         对于一般的单词首尾相连的问题,一般都是转化为有向图的欧拉通路问题(而不是无向图),比如”给你多个单词,问你能不能将所有单词组成这样一个序列:序列的前一个单词的尾字母与后一个单词的头字母相同”,如果你把每个单词看出无向的边,那么最终求出的欧拉通路可能存在两个单词尾部和尾部相连的情况。

无向欧拉图(半欧拉图)逆序打印欧拉回路或通路代码:

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<iomanip>
#include<list>
#include<map>
#include<queue>
#include<sstream>
#include<stack>
#include<string>
#include<set>
#include<vector>
using namespace std;
#define PI acos(-1.0)
#define EPS 1e-8
#define MOD 1e9+7
#define LL long long
#define ULL unsigned long long     //1844674407370955161
#define INT_INF 0x7f7f7f7f      //2139062143
#define LL_INF 0x7f7f7f7f7f7f7f7f //9187201950435737471
const int dr[]={0, 0, -1, 1, -1, -1, 1, 1};
const int dc[]={-1, 1, 0, 0, -1, 1, -1, 1};
// ios::sync_with_stdio(false);
// 那么cin, 就不能跟C的 scanf,sscanf, getchar, fgets之类的一起使用了。
const int maxn=100+5;

//无向图打印欧拉路径或回路
//输入保证是一个n顶点,m条边的具有欧拉回路或欧拉路径的无向图

int n;//图中顶点,顶点编号1到n
int m;//图中边
int G[maxn][maxn];
int vis[maxn][maxn];//vis[i][j]==1表示i与j点直接存在一条边

//打印欧拉路径或欧拉回路(必须本图有欧拉路径或回路才行)
//打印的结果中最后一条边才是u开始的,打印的第一条边不一定是u开始的
//如果是打印欧拉路径,那么输入的u一定要是起点之一,即度数为奇数的点之一
//否则euler打印的的边不会构成欧拉路径,只不过是乱序打印图中所有的边而已
void euler(int u)
{
    for(int v=1;v<=n;v++)if(vis[u][v]||vis[v][u])
    {
        //递归思想,去掉了u与v这条边,
        //余图还是一个具有欧拉道路的图,且v变成一个起点了
        vis[u][v]=vis[v][u]=0;
        euler(v);
        printf("%d %d\n",u,v);
    }
}

//输出欧拉回路或路径上按顺序经过的节点
//u也必须要是起点之一,否则输出的也是乱序点而已
void euler_point(int u)
{
    for(int v=1;v<=n;v++)if(vis[u][v] || vis[v][u])
    {
        vis[u][v]=vis[v][u]=0;
        euler_point(v);
    }
    printf("%d\n",u);
}

int main()
{
    while(scanf("%d%d",&n,&m)==2)
    {
        memset(G,0,sizeof(G));
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            G[u][v]=G[v][u]=1;//无向图
            vis[u][v]=vis[v][u]=1;
        }

        int u;
        scanf("%d",&u);
        euler_point(u);

    }
    return 0;
}

有向欧拉图(半欧拉图)逆序打印欧拉回路或通路代码:
 

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<iomanip>
#include<list>
#include<map>
#include<queue>
#include<sstream>
#include<stack>
#include<string>
#include<set>
#include<vector>
using namespace std;
#define PI acos(-1.0)
#define EPS 1e-8
#define MOD 1e9+7
#define LL long long
#define ULL unsigned long long     //1844674407370955161
#define INT_INF 0x7f7f7f7f      //2139062143
#define LL_INF 0x7f7f7f7f7f7f7f7f //9187201950435737471
const int dr[]={0, 0, -1, 1, -1, -1, 1, 1};
const int dc[]={-1, 1, 0, 0, -1, 1, -1, 1};
// ios::sync_with_stdio(false);
// 那么cin, 就不能跟C的 scanf,sscanf, getchar, fgets之类的一起使用了。
const int maxn=100+5;

//有向图打印欧拉路径或回路
//输入保证是一个n顶点,m条边的具有欧拉回路或欧拉路径的有向图

int n;//图中顶点,顶点编号1到n
int m;//图中边
int G[maxn][maxn];
int vis[maxn][maxn];//vis[i][j]==1表示i到j点存在一条边

//打印欧拉路径或欧拉回路(必须本图有欧拉路径或回路才行)
//打印的结果中最后一条边才是u开始的,打印的第一条边不一定是u开始的
//如果是打印欧拉路径,那么输入的u一定要是起点之一,即abs(入度-出度)==1
//且如果是出度-入度==1的点,那么该点就应该是欧拉图的起点,但是会逆序输出,所以最后才输出
//否则euler打印的的边不会构成欧拉路径,只不过是乱序打印图中所有的边而已
void euler(int u)
{
    for(int v=1;v<=n;v++)if(vis[u][v])
    {
        //递归思想,去掉了u与v这条边,
        //余图还是一个具有欧拉道路的图,且v变成一个起点了
        vis[u][v]=0;
        euler(v);
        printf("%d %d\n",u,v);
    }
}

//逆序输出欧拉回路或路径上按顺序经过的节点
//u也必须要是起点之一,否则输出的也是乱序点而已
void euler_point(int u)
{
    for(int v=1;v<=n;v++)if(vis[u][v])
    {
        vis[u][v]=0;
        euler_point(v);
    }
    printf("%d\n",u);
}

int main()
{
    while(scanf("%d%d",&n,&m)==2)
    {
        memset(G,0,sizeof(G));
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            G[u][v]=1;//有向图
            vis[u][v]=1;
        }

        int u;
        scanf("%d",&u);
        euler(u);

    }
    return 0;
}

 

猜你喜欢

转载自blog.csdn.net/sdau_fangshifeng/article/details/83443325