图论-----------欧拉通路以及欧拉回路

前言:

我们先来了解一下概念:

  • 欧拉通路:通过图中所有边一次且仅一次行遍所有顶点的通路
  • 欧拉回路:通过图中所有边一次且仅一次行遍所有顶点的回路
  • 欧拉图:具有欧拉回路的图
  • 半欧拉图:具有欧拉通路而无欧拉回路的图
  • 奇度点:与点相连的边的数目为奇数的点
  • 偶度点:与点相连的边的数目为偶数的点

判定

无向图

  • 欧拉通路:图是连通的,图中只有两个奇度点,分别是欧拉通路的两个端点(就是起点和终点),对于欧拉通路,除起点、终点外,每个点如果进入,显然一定要出去,因此都是偶点

  • 欧拉回路:图是连通的,点均为偶度点,对于欧拉回路,每个点进入、出去的次数相等,因此没有奇点

  • 无向图中不考虑初度和入度,直接归为 ‘度’

有向图

  • 欧拉通路:图是连通的,除两顶点外其余点的入度等于出度,这里我们可以设置出度为-1,入度为1,那么除了起点和终点外,其它点的度数为0;起点的度数就为-1,终点的度数就为1;

  • 欧拉回路:图是连通的,图中所有点的入度等于出度,同样我们设出度为-1,入度为1,那么所有的点的度数均为0。

欧拉回路包含着欧拉通路,这点要知晓!!!!!

在这里插入图片描述

做题步骤:

  • 首先应该根据题意是否判断图是连通的,即所有点是否在一个图上,这里可以用并查集来判断图是不是连通的。
  • 然后根据读入的边建立图,可以用链式前向星或者二维矩阵建图,然后记录点的度数来判断是不是欧拉回路或者欧拉通路。
  • 如果题目还要求路径,那么可以用递归版本dfs来求,或者非递归版本,后面的例题中会讲到。

并查集判断无向图中是否存在欧拉回路

例题集合

例题一:并查集判断无向图中是否存在欧拉回路

在这里插入图片描述
在这里插入图片描述
当给出一个无向图时,若要求判断图中是否存在欧拉回路,可以使用并查集判断图是否连通,并统计每个节点的度数,依次来判断是否存在。

若图连通且所有点的度数为偶数,则说明该无向图中存在欧拉回路。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<queue>
#include<iomanip>
using namespace std;
const int N  = 1010;
const int M = 1e5+10;
int head[N],to[M],nex[M],idx;
int du[N];
int fa[N];
void add(int x,int y){
	nex[++idx] = head[x];
	to[idx] = y;
	head[x] = idx;
	++du[x];
}
int find(int u){
	return fa[u]==u ? u : fa[u] = find(fa[u]); 
}
void mergy(int x,int y){
	int t1 = find(x);
	int t2 = find(y);
	if(t1!=t2){
		fa[t1] = t2;
	}
	return ;
}
int main()
{
	int n,m;
	while(cin>>n && n ){
		cin>>m;
		memset(head,0,sizeof(head));
		idx = 1;
		memset(du,0,sizeof(du));
		for(int i=1;i<=n;++i){
			fa[i] = i;
		}
		
		int x,y;
		for(int i=1;i<=m;++i){
			cin>>x>>y;
			add(x,y);
			add(y,x);
			mergy(x,y);
		}
		int sum = 0;
		for(int i=1;i<=n;++i){
			if(fa[i]==i){
				sum++;
			}
		}
		int flag = 0;
		if(sum>1){
			flag = 1;
		}
		else{
			for(int i=1;i<=n;++i){
				if(du[i]&1){
					flag = 1;
					break;
				}
			}
		}
		
		
		if(flag){
			cout<<0<<endl;
		}else{
			cout<<1<<endl;
		}
	}	
}

例题二:在这里插入图片描述
在这里插入图片描述

思路:就用上面的步骤来解题即可。

# include <iostream>
# include <algorithm>
# include <stack>
using namespace std;
const int N = 50100,M = 10010;
int head[M],to[N*2],nex[N*2],idx;
int vis[N*2];

int path[N*2+1000],id=0;

void add(int x,int y){   //链式前向星建图
	nex[++idx] = head[x]
	to[idx] = y;
	head[x] = idx;
}
int q[N*2+1000],top=0;


void eular(){    //这是非递归的版本
	q[++top] =1;      //第一步让你的起点入栈,这里的起点设置1 ,这是题目要求的
	while(top>0){    //如果没要求的话,随你设置,得到就是从起点x到终点x的路径
		int t = q[top];    //注意,我们得到是反向的路径,即终点到起点,因为是栈
		int i = head[t];    //取出头,但不去掉头
		while(i && vis[i]) i = nex[i];     //找到当前点未被访问过的边
		if(i){
			vis[i] = 1;    //设置当前边已访问过
			//vis[i^1] = 1 ;   //如果是无向图,那么这句话就加上
			head[t] = nex[i];   //更改当前点的下一条边的去向
			q[++top] = to[i];   //入栈
		}else{      //这里就是如果当前点连接的边已经全部遍历完,那么就出栈
			top--;
			path[++id] = t;   //把路径放进数组里,然后输出
		}
	}
}

void dfs(int u){
	for(int i=head[u];i;i=nex[i]){
		if(!vis[i]){
			vis[i] = 1;
			vis[i^1] = 1;
			dfs(to[i]);	
		}
	}
	cout<<u<<endl;
}
int main(void)
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=m;++i){
		int x,y;
		cin>>x>>y;
		add(x,y);   //这里是无向图,所以正反建一次
		add(y,x);
	}
	
	eular();
	
	for(int i=1;i<=id;++i){
		cout<<path[i]<<endl;
	}
	
	return 0;
 } 


(摘录别人的博客,请点击!
对于一般的单词首尾相连的问题,通常都是转化为有向图的欧拉通路问题。

例如:给出多个单词,问能不能将所有单词组成一个序列,序列的前一个单词的尾字母与后一个单词的头字母相同

如果把每个单词看出无向的边,那么最终求出的欧拉通路可能存在两个单词尾部和尾部相连的情况。

无向欧拉图打印欧拉通路/回路
输入保证一个有 n 个点,m 条边的具有欧拉回路或欧拉路径的无向图,要求打印出图的欧拉回路或通路。

如果要打印欧拉通路,输入的 start 一定要是起点之一,即:deg(start)=1,否则只是乱序打印图中所有的边。(无向图也是一样。)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<stack>
#define N 1001
using namespace std;
int n,m;
int G[N][N];
bool vis[N][N];//vis[i][j]=1表示点i到j之间存在一条边
void euler(int x){//打印以x为起点的欧拉回路或通路
    for(int y=1;y<=n;y++){
        if(vis[x][y]||vis[y][x]){
            vis[x][y]=0;//去掉x-y这条边
            vis[y][x]=0;//去掉y-x这条边
            euler(y);
 
            //首尾相连逆序打印欧拉通路
            //printf("%d %d\n",y,x);
        }
    }
 
    //逆序打印欧拉回路
    printf("%d ",x);
}
int main(){
 
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        G[x][y]=1;
        G[y][x]=1;
 
        vis[x][y]=1;
        vis[y][x]=1;
    }
 
    int start;
    scanf("%d",&start);
    euler(start);//打印以x为起点的欧拉回路或通路
 
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43743711/article/details/108268923
今日推荐