欧拉回路&&欧拉路径输出模版
知识点:
(1)欧拉路径定义:从一个点出发,不重不漏的经过图中每一条边的一条路径(允许多次经过同一个点)。
(2)欧拉路径的判断:
···········<1>若为无向图,则需连通,且图中恰好存在两个点的度数是奇数,其他节点的度数为偶数,这两个度数为奇数的点就是起点与终点;或者所有点度数都是偶数。
···········<2>若为有向图,则需连通,且图中恰好存在一个点入度比出度多一,一个点出度比入度多一,其他节点的出度等于入度;或者所有点入度等于出度。
(3)欧拉回路定义:起点和终点是一个点的欧拉路径。
(4)欧拉回路的判断:
············<1>若为无向图,则需连通,且所有点的度数都是偶数。
············<2>若为有向图,则需连通,且所有点的入度等于出度。
(5)通俗解释:欧拉路径就是一笔画,欧拉回路是特殊的一笔画:起点和重点重合的一笔画。
代码实现:Hierholzer算法
(1)首先我们要确定出起点,然后从起点开始不走重复边的递归。
(2)对于每一个点,当访问完所有的边时,把这个点加入答案序列。
(3)最后,输出答案序列即为一条欧拉路径依次经过的点
伪代码
void dfs(int u){
for(){ //遍历所有与u相邻的点v
if(!vis[v]){ //u与v的这条边没被访问过
vis[v]=1;
dfs(v); //递归与u相邻的点v
}
}
s.push(u); //没有边了,将u进入序列
}
but这里出现了一个问题:
这样写时间复杂度是不对的。按照算法,我们不能走重复边,但如果每次都vis判断一遍,则会出现很多次访问同一条边,时间复杂度是不正确的。
处理:
对于任意一个点 u 来说,建完图之后访问边是有顺序的,我们新开一个数组 delete delete 记录访问到哪个位置即可(即该删除哪个位置)。
是否存在欧拉路径&&欧拉回路只要按照上文给出的判定方法实现就可以了
上代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int ru[N],chu[N],n,m;
stack <int> s;
int delet[N];
vector <int> a[N];
void dfs(int u)
{
for(int i=delet[u];i<a[u].size();i=delet[u])//遍历从u能够到达的点
{
delet[u]=i+1;
dfs(a[u][i]);//搜索走到的点
}
s.push(u);//所有能到达的点都被处理过后,直接把u入栈
}
int main()
{
int u,v,n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i)
{
scanf("%d%d",&u,&v);
a[u].push_back(v);
chu[u]++;
ru[v]++;
}//输入的同时统计每个点的入度和出度
for(int i=1;i<=n;++i)
{
sort(a[i].begin(),a[i].end());
}//升序排列每一个点能达到的点的编号(用来应对字典序最小这一条件)
int start=1;//起点位置预设为1(如果没有字典序的要求那么设在任意一个点都可以)
bool flag=1;//flag==1表示存在欧拉回路,flag==1表示不存在欧拉路径或存在不是欧拉回路的欧拉路径
int sum1=0,sum2=0;//sum1是“入度=出度-1”(有可能作为起点)的点的个数,sum2是“入度=出度+1”(有可能作为终点)的点的个数
for(int i=1;i<=n;++i)
{
if(chu[i]!=ru[i]) flag=0;//判断是否是欧拉回路
if(chu[i]-ru[i]>1)//出度入度之差大于1,肯定没有欧拉路径
{
printf("No");
return 0;
}
if(chu[i]-ru[i]==1)//统计sum1个数
{
start=i;
sum1++;
}
if(ru[i]-chu[i]==1) sum2++;//统计sum2个数
}
if(!(flag==1||(sum1==sum2)&&sum1==1))//不是欧拉回路或者普通欧拉路径中的任意一个
{
printf("No");
return 0;
}
dfs(start);//从起点开始不重不漏的dfs
while(!s.empty())
{
printf("%d ",s.top());
s.pop();
}//按序输出栈里的所有内容,即为所求
return 0;
}