先普及一下概念:
如果图G中的一个路径包括每个边恰好一次,则该路径称为欧拉路径(Euler path)。
如果一个回路是欧拉路径,则称为欧拉回路(Euler circuit)。
具有欧拉回路的图称为欧拉图(简称E图)。具有欧拉路径但不具有欧拉回路的图称为半欧拉图。
判断一个图是否存在欧拉回路:
无向图:图中度数为奇数的点的数量为0,也就是一笔画问题中起点和终点可以随便选一个且为同一个的那种。
有向图:图中所有点的入度等于出度。
判断一个图是否存在欧拉路径:
无向图:图中度数为奇数的点的数量为0或2,也就是经典的一笔画问题。
有向图:图中除了起点和终点所有点的入度等于出度,起点的入度比出度小1,终点的入度比出度大1。
代码如何判断是否存在?
很简单,在读入边的时候O(m)记录入度和出度,之后O(n)扫一遍就好了,这边不举出代码了。
如何找到一条欧拉路径?
这里介绍一个方法,好像叫Hierholzer 算法:
从起点开始dfs,然后不走重复的边,发先走不了的时候回溯,在回溯的时候把这时访问的节点放到一个栈里,然后等dfs结束后应该栈里有m个节点(通常m>n因此有些点被重复记录,这也正是我们想要的)
然后你会惊奇的发现,将这m个节点的出栈顺序便是一条可行的欧拉路径!
代码:(以无向图为例,有向图不建反向边即可)
#include<cstdio>
#include<cstring>
const int N=110000,M=220000;
struct node{
int x,y,next;
}data[M];//邻接表
int n,m,num,s;
int h[N];//邻接表
bool flag[M];//访问边标记
int d[N];//degree度数
int b[M],top;//不知道起什么名字好
void ins(int x,int y)
{
data[++num].x=x;
data[num].y=y;
data[num].next=h[x];
h[x]=num;
}
void dfs(int u)
{
for(int i=h[u];i!=-1;i=data[i].next)
{
if(flag[i]) continue;
int v=data[i].y;
flag[i]=true;
flag[i^1]=true;//反向边也打上标记
dfs(v);
b[++top]=v;
}
}
int main()
{
scanf("%d%d",&n,&m);
num=-1;//这样使用^可以直接得到反向边
memset(h,0xff,sizeof h);//全赋-1
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
ins(x,y);d[x]++;
ins(y,x);d[y]++;
}
int num1=0;
for(int i=1;i<=n;i++)
if(d[i]&1)
{
num1++;
if(!s) s=i;
}
if(num1!=0 && num1!=2)
{
printf("NO\n");//不存在欧拉路径
return 0;
}
printf("YES\n");//存在欧拉路径/回路
if(!s) s=1;//存在欧拉回路
dfs(s);
b[++top]=s;//起点入栈
while(top) printf("%d ",b[top--]);//出栈
return 0;
}
应该没写错吧,如果写错了请告诉我谢谢
好像这是我第一次发表博客的样子