欧拉回路整理
chunlvxiong的博客
一、定义:
若一个图G存在一条路,经过G中每条边有且仅有一次,称这条路为欧拉路。
如果这是一条回路称为欧拉回路,同时这个图G称为欧拉图。
二、判定方法
无向图:
欧拉路:图连通,有2个奇点,欧拉路从一个奇点出发,到另一个奇点结束。
欧拉回路:图连通,有0个奇点,欧拉回路可从任意点开始。
有向图:
欧拉图:图连通,有一个点X出度=入度+1,另一个点Y入度=出度+1,其余点出度=入度,欧拉路从X点开始,到Y点结束。
欧拉回路:图连通,所有点出度=入度,欧拉回路可从任意点开始。
三、输出路径
这里考虑如何输出无向图的欧拉路或欧拉回路(假设存在)。
首先要从一个正确的点开始DFS(实际上这个点是作为结束点的)。然后对于每个点DFS其所有连边所到的点,且每次DFS之前删除掉那条边。当所有DFS完成之后输出这个点即可。
个人理解:
称一个奇点S到另一个奇点T的路称为主路径,则其余所有路径都是环。
从S开始DFS,如果碰到的边通向的是一条环,自然最后会回到S点,由于S点的连边并没有全部删光,所以这个环不会打出。如果碰到的边通向的是主路径,那么它会直接打出从T到S的路径(当然中间附带的环也会打好),即它会优先保证打出主路径,然后再打出环,因而不会导致错误。
实现的时候使用链表而不是邻接矩阵以提高效率。
贴代码:
#include <bits/stdc++.h> const int maxn=105; const int maxm=1005; int n,m,tot,head[maxn],last[maxn]; struct E{ int to,next; }edge[maxm*2]; int D[maxn]; bool vis[maxm]; void init(){ memset(head,0,sizeof(head)),tot=0; } void makedge(int u,int v){ edge[++tot].to=v; edge[tot].next=head[u]; head[u]=tot; } void dfs(int u){ for (int i=last[u];i;i=edge[i].next) if (vis[(i-1)>>1]) last[u]=edge[last[u]].next; else{ vis[(last[u]-1)>>1]=1; dfs(edge[last[u]].to); last[u]=edge[last[u]].next; } printf("%d\n",u); } int main(){ scanf("%d%d",&n,&m),init(); memset(D,0,sizeof(D)); for (int i=1,x,y;i<=m;i++){ scanf("%d%d",&x,&y); makedge(x,y),makedge(y,x); D[x]++,D[y]++; } int ODD=0,s=1; for (int i=1;i<=n;i++){ last[i]=head[i]; if (D[i]&1) ODD++,s=i; } if (ODD==0 || ODD==2){ memset(vis,0,sizeof(vis)); dfs(s); } else puts("No Eular path!"); return 0; }
另类实现(网上广为流传的版本):
贴代码:
#include <bits/stdc++.h> const int maxn=105; const int maxm=1005; int n,m,tot,head[maxn],last[maxn]; struct E{ int to,next; }edge[maxm*2]; int top,stack[maxn],D[maxn]; bool vis[maxm]; void init(){ memset(head,0,sizeof(head)),tot=0; } void makedge(int u,int v){ edge[++tot].to=v; edge[tot].next=head[u]; head[u]=tot; } void dfs(int u){ stack[++top]=u; for (int i=last[u];i;i=edge[i].next) if (vis[(i-1)>>1]) last[u]=edge[last[u]].next; else break; if (last[u]){ vis[(last[u]-1)>>1]=1; dfs(edge[last[u]].to); } } void fleury(int x){ stack[top=1]=x; while (top){ int u=stack[top]; for (int i=last[u];i;i=edge[i].next) if (vis[(i-1)>>1]) last[u]=edge[last[u]].next; else break; if (!last[u]) printf("%d ",stack[top--]); else dfs(stack[top--]); } } int main(){ scanf("%d%d",&n,&m),init(); memset(D,0,sizeof(D)); for (int i=1,x,y;i<=m;i++){ scanf("%d%d",&x,&y); makedge(x,y),makedge(y,x); D[x]++,D[y]++; } int ODD=0,s=1; for (int i=1;i<=n;i++){ last[i]=head[i]; if (D[i]&1) ODD++,s=i; } if (ODD==0 || ODD==2){ memset(vis,0,sizeof(vis)); fleury(s); } else puts("No Eular path!"); return 0; }