版权声明:本文为CSDN博主「Alex_McAvoy」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u011815404/article/details/86590498
目录
【基本概念】
- 欧拉通路:通过图中所有边一次且仅一次行遍所有顶点的通路
- 欧拉回路:通过图中所有边一次且仅一次行遍所有顶点的回路
- 欧拉图:具有欧拉回路的图
- 半欧拉图:具有欧拉通路而无欧拉回路的图
- 奇度点:与点相连的边的数目为奇数的点
- 偶度点:与点相连的边的数目为偶数的点
【欧拉通路/回路的判定】
1.无向图
1)欧拉通路:图是连通的,图中只有两个奇度点,分别是欧拉通路的两个端点
对于欧拉通路,除起点、终点外,每个点如果进入,显然一定要出去,因此都是偶点
2)欧拉回路:图是连通的,点均为偶度点
对于欧拉回路,每个点进入、出去的次数相等,因此没有奇点
2.有向图
1)欧拉通路:图是连通的,除两顶点外其余点的入度等于出度,且这两个顶点中,一个顶点入度比出度大1(起点),另一个入度比出度小1(终点)
2)欧拉回路:图是连通的,图中所有点的入度等于出度。
3.并查集判断无向图中是否存在欧拉回路
当给出一个无向图时,若要求判断图中是否存在欧拉回路,可以使用并查集判断图是否连通,并统计每个节点的度数,依次来判断是否存在。
若图连通且所有点的度数为偶数,则说明该无向图中存在欧拉回路。
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 1001
using namespace std;
int n, m ;
int degree[N];
int father[N];
int Find(int x)
{
if(father[x]==-1)
return x;
return father[x] = Find(father[x]);
}
void Union(int x,int y)
{
x = Find(x);
y = Find(y);
if(x!=y)
father[x] = y;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(degree, 0, sizeof(degree));
memset(father, -1, sizeof(father));
for(int i = 1; i <= m; i++)
{
int x, y;
scanf("%d%d", &x, &y);
degree[x]++;
degree[y]++;
Union(x,y);
}
int cnt = 0; //记录连通分量
for(int i = 1; i <= n; i++)
if(Find(i)==i)
cnt++;
if(cnt!=1) //若cnt大于1,说明图不连通
printf("NO\n");
else
{
int num = 0; //统计度数为奇数的点
for(int i = 1; i <= n; i++)
if(degree[i]&1)
num++;
if(num==0)
printf("YES\n");
else
printf("NO\n");
}
}
return 0;
}
4.Fleury 算法
Fleury 算法是用于求无向图中欧拉回路的算法,其基本思想如下:
1、任取图 中一顶点 ,令
2、假设沿 ,走到顶点 ,按下面的方法从 中选择 :
- 与 相关联
- 除非无别的边可供选择,否则 不应是 中的桥
3、当 2 不能再进行时,算法停止
可以证明的是,当算法停止时,所得到简单回路 为 中的欧拉回路。
int n, m;
int start; //起点
int num; //奇度顶点个数
int degree[N]; //顶点的度
int path[N]; //存储欧拉回路
int cnt; //欧拉回路计数器
bool G[N][N];
stack<int> S;
void dfs(int x)
{
S.push(x);
for(int i = 1; i <= n; i++)
{
if(G[x][i])
{
G[x][i] = false;
G[i][x] = false;
dfs(i);
break;
}
}
}
void Fleury(int x)
{
S.push(x);
while(!S.empty())
{
bool flag = false;
for(int i = 1; i <= n; i++)
{
if(G[S.top()][i]==true) //与起点有关联的边
{
flag = true;
break;
}
}
if(flag==true) //如果有关联的边
{
int temp = S.top();
S.pop();
dfs(temp);
}
else //如果没有有关联的边
{
path[cnt++] = S.top();
S.pop();
}
}
}
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;
degree[x]++;
degree[y]++;
}
cnt = 1;
num = 0;
start = 1;
for(int i = 1; i <= n; i++)
{
if(degree[i]&1) //如果某点的度是奇数
{
if(num==0) //记录起点
start = i;
num++; //奇度点个数+1
}
}
if(num==0||num==2) //如果存在奇度顶点,则从奇度顶点出发,否则从顶点0出发
{
Fleury(start);
for(int i = cnt-1; i >= 1; i--)
printf("%d ", path[i]);
printf("\n");
}
else
printf("No Euler path.\n");
return 0;
}
【应用】
对于一般的单词首尾相连的问题,通常都是转化为有向图的欧拉通路问题。
例如:给出多个单词,问能不能将所有单词组成一个序列,序列的前一个单词的尾字母与后一个单词的头字母相同
如果把每个单词看出无向的边,那么最终求出的欧拉通路可能存在两个单词尾部和尾部相连的情况。
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",x,y);
}
}
//逆序打印欧拉回路
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;
}
2.有向欧拉图打印欧拉通路/回路
输入保证是 个点, 条边的具有欧拉回路或欧拉路径的有向图,要求打印出图的欧拉回路或通路。
如果要打印欧拉通路,那么输入的 一定要是起点之一,即:,否则只是乱序打印图中所有的边。
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[x][y] = 0; //去掉x-y这条边
euler(y);
//首尾相连逆序打印欧拉通路
//printf("%d %d\n",x,y);
}
}
//逆序打印欧拉回路
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;
vis[x][y] = 1;
}
int start;
scanf("%d", &start);
euler(start); //打印以x为起点的欧拉回路或通路
return 0;
}
2)邻接链表实现
struct Edge {
int endd;
bool vis;
Edge(int endd,bool vis):endd(endd),vis(vis){}
};
int n, m;
vector<Edge> G[N];
void euler(int x)
{
for(int i = G[x].size()-1; i >= 0; i--)
{
if(!G[x][i].vis)
{
G[x][i].vis = 1;
euler(G[x][i].endd);
}
}
printf("%d\n", 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].push_back(Edge(y,false));
G[y].push_back(Edge(x,false));
}
int start = 1;
euler(start);
return 0;
}
来源:CSDN
作者:菜是原罪QAQ
链接:https://blog.csdn.net/qq_42815188/article/details/104109330