欧拉回路算法

1 基本概念

1.1 欧拉路径和欧拉回路

欧拉回路: 在一个图中,由i点出发,将每个边遍历一次最终回到出发点i的一条路径。具有欧拉回路的图称为欧拉图

无向图
存在欧拉回路的充要条件是所有的点的度数均为偶数
因为每个点的度数为偶数,所以可以将整个图看做由数个环嵌套而成,因为环一定能找到一条欧拉回路,所以整个图也能找到欧拉回路。
有向图
存在欧拉回路的充要条件是所有的点的出度均等于入度
对于每一个点,每次进入这个节点,就一定有一条路可以出去,因此必定存在一条欧拉回路。

欧拉路径: 在一个图中,由i点出发,将每个边遍历一次最终到达j点的一条路径。

1、欧拉回路的情况 。
2、所有点中出度比入度大1的点有一个,入度比出度大1的点有一个,不允许有大几个的情况。

2 欧拉回路判定算法

2.1 Fleury(弗罗莱) 算法

Fleury算法用来判断图是否是欧拉通路或欧拉回路的算法。
使用如下的欧拉图,了解Fleury算法的主要步骤。
在这里插入图片描述

  • 选节点1为起点,并将该起点加入路径中。Fleury算法选择栈存储欧拉路径。
    在这里插入图片描述
  • 从起点开始,一路DFS试着走出一条通路。方法是找与此节点相邻的节点。
    如果只有一个节点,则将这个点直接加入路径中。
    如果有多个相邻节点,则选择其中一条边,把相邻节点加入路径后,且删除这一条边。
    如果没有邻接节点,则从路径中弹出。
    节点5和节点2都与1相邻,可以选择向5方向,也可以选择2方向。这里选择2方向,把节点2放入路径,然后置1-2这条边为删除状态。如此这般,一路经过3、4、5节点后回到1号节点。下图中标记为红色的边表示已经访问或被删除。
    在这里插入图片描述
  • 重新回到节点1,此时不再存在与节点1邻接的节点,从路径中弹也,依次可弹出5、4、3。直到碰到2号节点。
    在这里插入图片描述
  • 因为存在与2号节点邻接的节点,再次以2号节点为始点,使用DFS开路。一路上遇到6、7,且再次回到2号节点。
    在这里插入图片描述
  • 2号节点不存在与之邻接的节点,出栈。同理,7、6依次出栈。
    在这里插入图片描述
    小结:
    当有与当前节点邻接的节点时,一路DFS,直到没有邻接的尽头。些时,一轮DFS算法结束,从路径中依次弹出没有邻接节点的节点,直到遇到还有邻接节点的节点,新一轮的DFS重新开始。直到所有节点邻接的边全部访问完毕。
    编码实现:
#include <iostream>
#include <math.h>
#include <algorithm>
#include <cstring>
#include <stack>
#define INF 100000
using namespace std;
int graph[100][100];
int n,m;
stack<int> sta;
void read() {for(int i = 0; i < m; i++) {int f,t;cin >> f >> t;graph[f][t] = 1;graph[t][f] = 1;}
}
void dfs(int u) {sta.push(u);for(int i = 1; i <= n; i++) {if(graph[i][u] > 0) {//标记为删除graph[u][i] = 0;graph[i][u] = 0;dfs(i);//仅朝一条边方向 DFS,方便形成回路 break;}}
}
void fleury(int x) {int  isEdge;sta.push(x);while(!sta.empty()) {isEdge = 0;int t = sta.top();sta.pop();//检查是否有边for(int i = 1; i <= n; i++) {if(graph[t][i] > 0) {isEdge = 1;break;}}if(isEdge == 0) {//没有邻接边,输出cout << t << " ";} else {//有邻接边,一路DFS狂奔dfs(t);}}
}
int main() {cin >> n >> m;memset(graph,0,sizeof(graph));read();int num = 0;int start = 1;for(int i = 1; i <= n; i++) {int deg = 0;for(int j = 1; j <= n; j++)deg += graph[i][j];if(deg % 2 == 1) {//奇节点的数量start = i;num++;}}if(num == 0 || num == 2)fleury(start);elsecout << "不存在欧拉路径" << endl;return 0;
}
//测试用例
7 8
1 2
1 5
2 3
2 6
2 7    
3 4
4 5
6 7    

测试结果
在这里插入图片描述

2.2 Hierholzer 算法

也称逐步插入回路法。由数学家卡尔·希尔霍尔策给出,基于贪心思想。Hierholzer 的基本思路。先找到一个子回路,以此子回路为基础,逐步将其它回路以插入的方式合并到该子回路中,最终形成完整的欧拉回路。继续使用上图做演示。

  • 寻找子回路:如下从节点1开始,沿着边遍历图,一边遍历一边删除经过的边。如果遇到一个所有边都被删除的节点,那么该节点必然是 1(回到初始点)。将该回路上的节点和边添加到结果序列中。这个过程和Fleury算法没有太多区别。
    在这里插入图片描述

  • 回溯时检查刚添加到结果序列中的节点,看是否还有与节点相连且未遍历的边。可发现节点 2 有未遍历的边,则从 2 出发开始遍历,找到一个包含2 的新回路,将结果序列中的一个 2 用这个新回路替换,此时结果序列仍然是一个回路。这是和Fleury算法最大区别。
    在这里插入图片描述

  • 重复直到所有边都被遍历。

编码实现:

#include<iostream>
#include<string.h>
#include<vector>const int maxn = 10005;
const int maxm = 1000005;//edge
using namespace std;
int n,m;
struct Edge {int to, nxt;bool vis=0;
};
Edge edge[maxm];
//如果没有以 i 为起点的有向边则 head[i] 的值为 0
int head[maxm];
//边的个数
int cnt;
//存储找到的回路
vector<Edge> ans;
//起始点
int sn;void init() {for(int i=1; i<=n; i++) {head[i]=0;cnt=0;}
}/*
*添加边
*/
void addEdge(int from, int to) {edge[cnt].to = to;edge[cnt].nxt = head[from];head[from] = cnt++;
}
void read() {int f,t;for(int i=1; i<=m; i++) {cin>>f>>t;addEdge(f,t);addEdge(t,f);}
}
void hierholzer(int sn) {for (int i = head[sn]; i != 0; i = edge[i].nxt) {// 遍历过if (edge[i].vis) continue;// 删除edge[i].vis = edge[i ^ 1].vis = true;// 继续hierholzer(edge[i].to);// 回溯时加入结果序列后,循环会继续查找是否有邻接边ans.push_back(edge[i]);}
}
void show() {for(int i=0; i<ans.size(); i++) {cout<<ans[i].to<<"\t";}cout<<sn<<"\t";
}int main() {cin>>n>>m;sn=1;init();read();hierholzer(sn);show();return 0;
}

测试结果:
在这里插入图片描述

3. 总结

Hierholzer和Fleury算法的基本思路差不多,在DFS时找环。Fleury使用分段策略,找到一条环后,以环中某一个还存在邻接边的节点重新开始使用DFS找环,直到找到所有环。Hierholzer算法很有技巧性,在回溯时检查节点是否还有邻接边,有则重新DFS直到完毕。

参考资料:
https://blog.csdn.net/y6123236/article/details/135020029

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/802625.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenHarmony实战:轻量系统STM32F407芯片移植案例

介绍基于STM32F407IGT6芯片在拓维信息Niobe407开发板上移植OpenHarmony LiteOS-M轻量系统&#xff0c;提供交通、工业领域开发板解决方案。 移植架构采用Board与SoC分离方案&#xff0c;使用arm gcc工具链Newlib C库&#xff0c;实现了lwip、littlefs、hdf等子系统及组件的适配…

Todesstern:一款针对注入漏洞识别的强大变异器引擎

关于Todesstern Todesstern是一款功能强大的变异器引擎&#xff0c;该工具基于纯Python开发&#xff0c;该工具旨在辅助广大研究人员发现和识别未知类型的注入漏洞。 Todesstern翻译过来的意思是Death Star&#xff0c;即死亡之星&#xff0c;该工具是一个变异器引擎&#xff…

【剪映专业版】03云空间扩容

视频课程&#xff1a;B站有知公开课【剪映电脑版教程】 个人云空间&#xff1a;多端同步及素材、草稿保存 云空间默认为512M&#xff0c;可以免费提升至3GB 访问剪映官网-全能易用的桌面端剪辑软件-轻而易剪 上演大幕&#xff0c;后进入工作台 点击消息 小组云空间&#xff…

视频图像的两种表示方式YUV与RGB(2)

前一篇文章具体介绍了视频图像的两种表示方式&#xff0c;此篇详细介绍下YUV的采样格式及其对图像视频的表示方式。 常见YUV有很多规格&#xff0c;例如YUV444&#xff0c;YUV422和YUV420&#xff0c;后面的数字是表示采样的比例。其中YUV420是FFmpeg里最常用的&#xff0c;因为…

004_文本分析与挖掘(jieba库三种分词模式)

jieba库 一、概述 jieba 库的分词原理是利用一个中文词库&#xff0c;将待分词的内容与分词词库进行比对&#xff0c;通过图结构和动态规划方法找到最大概率的词组&#xff1b;除此之外&#xff0c;jieba 库还提供了增加自定义中文单词的功能。 支持三种分词模式 1、精确模式…

Linux(CentOS7)部署 y-api 接口管理平台

目录 前言 前置环境 mongodb node 安装 y-api 部署页面 启动 y-api 基本使用教程 前言 前后端分离时代&#xff0c;前后端通过接口文档来协作开发项目。一般开发过程中&#xff0c;由后端先编写接口文档&#xff0c;然后交付给前端&#xff0c;这时候前后端都根据这个…

[数据概念|方案实操]最新案例-七个数据资产化案例解析

“ 数据资产化市场发展可称得上是如火如荼” 数据资产化市场快速发展&#xff0c;最近又涌现出一批创新案例&#xff0c;在这里跟大家做一个分享和解析&#xff0c;这里我们按照发生或报道时间顺序由近至远。 1.2024年3月21日&#xff0c;北京建院完成建筑数据资产模拟入表 2…

ML Kit:通过Mendix 集成人脸识别算法

预训练模型是一种已经使用训练数据集进行训练并包含执行模型所需所有参数的机器学习模型。这类模型常用于计算机视觉领域&#xff0c;比如可以在Mendix Studio Pro中导入ONNX模型后&#xff0c;可以在微流程中执行该模型。 本文讲述如何在Mendix应用程序中集成特定的人脸检测模…

OpenHarmony实战:帆移植案例(中)

OpenHarmony实战&#xff1a;帆移植案例&#xff08;上&#xff09; Audio服务介绍 服务节点 基于ADM框架的audio驱动对HDI层提供三个服务hdf_audio_render、hdf_audio_capture、hdf_audio_control。 开发板audio驱动服务节点如下&#xff1a; console:/dev # ls -al hdf_au…

【24年软考】信息系统项目管理师论文写作技巧(附范文10篇)

24年软考信息系统项目管理师论文写作准备&#xff1a; 论文准备时一定要紧扣考纲来进行&#xff0c;这样才能紧靠考试内容&#xff0c;不至于跑偏得不到高分。 1、多看论文范文&#xff0c;能够从别人的论文中快速熟悉写作的框架和思路。&#xff08;结尾有论文范文分享&…

数据库之DQL操作(数据查询语言)

DQL英文全称是Data Query Language(数据查询语言)&#xff0c;数据查询语言&#xff0c;用来查询数据库中表的记录。查询关键字: SELECT。 本节介绍以下表为例&#xff1a; create table emp(id int comment 编号&#xff0c;workno varchar(10) comment 工号&#xff0c;nam…

mybatis-plus与mybatis同时使用别名问题

在整合mybatis和mybatis-plus的时候发现一个小坑&#xff0c;单独使用mybatis&#xff0c;配置别名如下&#xff1a; #配置映射文件中指定的实体类的别名 mybatis.type-aliases-packagecom.jk.entity XML映射文件如下&#xff1a; <update id"update" paramete…

rabbitmq安装延时插件

rabbitmq安装延时插件 1、下载延迟插件 在 RabbitMQ 的 3.5.7 版本之后&#xff0c;提供了一个插件&#xff08;rabbitmq-delayed-message-exchange&#xff09;来实现延迟队列 &#xff0c;同时需保证 Erlang/OPT 版本为 18.0 之后。 我这里 MQ 的版本是 3.10.0 现在去 GitH…

深入浅出 -- 系统架构之微服务标准组件及职责

我们来认识一下微服务架构在Java体系中依托哪些组件实现的。 相对于单体架构的简单粗暴&#xff0c;微服务的核心是将应用打散&#xff0c;形成多个独立提供的微服务&#xff0c;虽然从管理与逻辑上更符合业务需要。但微服务架构也带来了很多急需解决的核心问题&#xff1a; 1…

Java项目中使用事务

事务的四大特性 事务特性ACID&#xff1a;原子性&#xff08;Atomicity&#xff09;、一致性&#xff08;Consistency&#xff09;、隔离性&#xff08;Isolation&#xff09;、持久性&#xff08;Durability&#xff09;。 原子性是指事务包含的所有操作要么全部成功&#x…

HarmonyOS 开发-实现Swiper高度可变化效果

介绍 在很多应用中&#xff0c;swiper组件每一个page的高度是不一致的&#xff0c;所以需要swiper组件下方页面的高度跟着一起变化。 效果图预览 使用说明 向左滑动swiper组件&#xff0c;上方swiper组件高度变高&#xff0c;下方页面随着swiper的变化而平滑的变化。 实现思…

第十三届蓝桥杯C/C++大学B组真题题解(一)

1、扫雷 #include <bits/stdc.h> using namespace std; int n,m; const int N110; int g[N][N]; int dx[8]{-1,-1,-1,0,1,1,1,0}; int dy[8]{-1,0,1,1,1,0,-1,-1}; int dfs(int x,int y){int ans0;for(int i0;i<8;i){int axdx[i],bydy[i];if(a<0||a>n-1||b<0…

【RHEL】redhat yum 报错: not registered to Red Hat Subscription Management.

【RHEL】redhat yum 报错: not registered to Red Hat Subscription Management. 问题描述解决方法参考博客&#xff1a; 问题描述 使用redhat7用yum install -y dos2unix命令时出现这个错误 This system is not registered to Red Hat Subscription Management. You can use …

乳腺癌诊断的集成自注意力Transformer编码器

ETECADx: Ensemble Self-Attention Transformer Encoder for Breast Cancer Diagnosis Using Full-Field Digital X-ray Breast Images 内科医生和放射科医生建议使用多种方法来发现乳腺癌&#xff0c;包括数字乳房x线摄影(DM)、超声(US)和磁共振成像(MRI)。 CAD系统与乳腺x线…

深度学习【向量化(array)】

为什么要向量化 在深度学习安全领域、深度学习练习中&#xff0c;你经常发现在训练大量数据时&#xff0c;深度学习算法表现才更加优越&#xff0c;所以你的代码运行的非常快至关重要&#xff0c;否则&#xff0c;你将要等待非常长的时间去得到结果。所以在深度学习领域向量化…