欧拉路径详解
什么是欧拉路径?欧拉路径就是一条能够不重不漏地经过图上的每一条边的路径,即小学奥数中的一笔画问题。而若这条路径的起点和终点相同,则将这条路径称为欧拉回路。
如何判断一个图是否有欧拉路径呢?显然,与一笔画问题相同,一个图有欧拉路径需要以下几个条件:
- 首先,这是一个连通图
- 若是无向图,则这个图的度数为奇数的点的个数必须是0或2;若是有向图,则要么所有点的入度和出度相等,要么有且只有两个点的入度分别比出度大1和少1
上面这两个条件很好证明。查找欧拉路径前,必须先保证该图满足以上两个条件,否则直接判误即可。
查找欧拉路径的算法有Fluery算法和Hierholzer算法。下面介绍一下Hierholzer算法。
算法流程:
- 对于无向图,判断度数为奇数的点的个数,若为0,则设任意一点为起点,若为2,则从这2个点中任取一个作为起点;对于有向图,判断入度和出度不同的点的个数,若为0,则设任意一点为起点,若为2,则设入度比出度小1的点为起点,另一点为终点。具体起点的选择要视题目要求而定。
- 从起点开始进行递归:对于当前节点x,扫描与x相连的所有边,当扫描到一条(x,y)时,删除该边,并递归y。扫描完所有边后,将x加入答案队列。
- 倒序输出答案队列。(因为这里是倒序输出,我们可以用栈来存储答案,当然用双端队列也可以)
解析:
从起点开始,每一次执行递归函数,相当于模拟一笔画的过程。递归的边界显然就是路径的终点,对于一个有欧拉路径的图,此时图上的所有边都已被删除,自然就不能继续递归。由于存储答案是在遍历以后进行的,答案存储也就是倒序的,因此要倒序输出答案。
代码:
#include<iostream>
#include<stack>
using namespace std;
const int N=500;
int n,tot,c=N,jp[N],cnt[N],edge[N][N];
char a,b;
stack<int> q;
void dfs(int now)
{
for(int i=1;i<=N;i++)
if(edge[now][i]==1)
{
edge[now][i]--,edge[i][now]--;
dfs(i);
}
q.push(now);//加入答案队列
}//算法过程
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a>>b;
c=min(c,a);
c=min(c,b);
edge[a][b]++,edge[b][a]++;
cnt[a]++;
cnt[b]++;//统计每个节点的度数
}
for(int i=1;i<=N;i++)
if(cnt[i]%2==1)
jp[tot++]=i;//找出度数为奇数的节点
if(tot!=2 && tot)
{
cout<<"No Solution";
return 0;
}//若该图没有欧拉路径则判误
int stat;
if(tot)
stat=min(jp[0],jp[1]);
else
stat=c;//找出起点
dfs(stat);
while(!q.empty())
{
char ct=q.top();
cout<<ct;
q.pop();
}//倒序输出
return 0;
}
在上面的代码中,找出的是起点字典序最小的欧拉路径,具体情况应视题意而定。
习题:
2019.4.16 于厦门外国语学校石狮分校