欧拉路径算法

欧拉路径算法

文章目录

  • 欧拉路径算法
    • 一、前言
    • 二、欧拉路径
      • [2.1 概念](#2.1 概念)
      • [2.2 逻辑梳理](#2.2 逻辑梳理)
      • [2.3 希尔霍尔策算法](#2.3 希尔霍尔策算法)
        • [2.3.1 基本思想](#2.3.1 基本思想)
        • [2.3.2 思路](#2.3.2 思路)
        • [2.3.3 代码](#2.3.3 代码)
      • [2.4 习题](#2.4 习题)
        • [2.4.1 洛谷](#2.4.1 洛谷)
    • 三、小结

一、前言

今天,要学的是欧拉路径~

二、欧拉路径

2.1 概念

对于一个连通的图G,有:

  • 欧拉路径 :Euler Path,一条路径,能够不重复遍历完所有的边,一笔画完所有的边,所以有些涉及到欧拉路径的问题叫一笔画问题。
  • 欧拉回路 :一条路径,能够不重复地遍历完所有的边,并且回到起点 。可以看出欧拉回路也是欧拉路径。(特殊的一类)
  • 半欧拉图:一个图,图中存在欧拉路径但不存在欧拉回路。
  • 欧拉图:也称E图,图中存在欧拉回路。可以看出欧拉图也是半欧拉图。

2.2 逻辑梳理

如何判断有没有欧拉路径/欧拉回路?

  • 无向图:度
    • 存在欧拉路径的充要条件:度数为奇数的点只能有0/2个,剩下的点全是偶数即可。(0个是欧拉回路,2个是欧拉路径)
    • 存在欧拉回路的充要条件:所有的点的度都得是0/偶数。
  • 有向图:入度和出度
    • 存在欧拉路径的充要条件:
      • 要么所有的点的出度均等于入度;
      • 要么除了两个点之外,其余所有点的出度=入度 剩余的两个点:一个满足出度-入度=1(起点) 一个满足入度-出度=1(终点)
    • 存在欧拉回路的充要条件:所有点的出度均等于入度

注:无向图可以判断为有向图,有向图更重要

怎么找到欧拉路径/欧拉回路?

  • 若没有明确告诉,先判断是不是欧拉图(度)
  • 找欧拉路径/回路(希尔霍尔策算法 H i e r h o l z e r Hierholzer Hierholzer)

欧拉回路和欧拉路径的区别:起点终点不一致

欧拉路径,起点和终点固定(起点的入度-出度=1

读完题发现找欧拉回路/路径就可以用希尔霍尔策算法~特别有针对性!!!

2.3 希尔霍尔策算法

2.3.1 基本思想

基于 D F S DFS DFS算法,又名"套圈法"

2.3.2 思路

以找欧拉回路为例:

  • 寻找子回路:从任意节点u出发,沿着边遍历图,遍历过程中,删除经过的边(打标记就行,还要减去度数)。如果遇到边都删除的节点,就回来了(回到u)。
  • 检查有没有其他回路:有没有点的边还没有被删完的,有的话,往另一个方向找。
  • 重复第二步,直至没有找到。

上图:

2.3.3 代码
  • 选择一个合理的点作为起始点,遍历所有相邻边。

  • 深度优先搜索,访问相邻顶点,将经过的边都不再访问。(打标记)

    把第一个步骤和第二个步骤合并到 D F S DFS DFS中

  • 如果当前顶点没有相邻边,则将顶点加入数组末尾。

  • 最后将数组倒叙输出,就是从起点出发的欧拉回路。

cpp 复制代码
// 基于无向图
#include<iostream>
#include<stack>
using namespace std;
struct Edge
{
    int v,nxt,eid;	// eid是边的编号
    bool del;		// =1表示这条边是否被遍历过了
}e[maxe];
//建图略。。。
// 为了方便我们查找某一条有向边的反向边,当我们向链式前向星中加入第i(i从1到m)条无向边x - y时,我们会将有向边 x->y 存储在e[i * 2],有向边 y->x 存储在e[i * 2 - 1],
// 这样,e[i]和e[i^1]就互为反向边
// 我们可以直观地发现,如果时一个偶数x^1,那么答案是偶数x + 1,如果是一个奇数x^1,那么答案是奇数x-1
// ans1是一个类型为vector<int> 的变量,用来存储我们找到的回路(边表示)
// ans2是一个类型为vector<int> 的变量,用来存储我们找到的回路(点表示)
void dfs(int x)
{
    for(int i=head[x];i!=-1;i=e[i].nxt)
    {
        // 这条边已经被遍历过了,跳过
        if(e[i].del)	continue;
        // 删除有向边e[i]和它的反向边e[i^1]
        e[i].del=e[i^1].del=true;// 遍历为走过的样子
        // 继续遍历相邻点
        dfs(e[i].v);
        // 将边e[i]加入结果序列中
        ans1.push_back(e[i],eid);
    }
    ans2.push_back(x);
}
// 倒着输出ans即为答案(数组存的答案)

注:

  • 终点一定是"死胡同"
  • 如果是欧拉回路,先 d f s dfs dfs或者先记录答案都行,因为是回路,可以走回来
  • 如果是欧拉路径,一定存在一个终点(死胡同)所以要先 D F S DFS DFS,后记录答案(入栈),此时可以保证记录答案时,该点的出边全部找完,相当于从终点到起点记录答案,可以保证x到终点的路径已经记录,此时x是未记录路径中的终点,避免出错。
  • 正着走,可能会出错,倒着走不会

2.4 习题

2.4.1 洛谷
  • P2731 [USACO3.3] 骑马修栅栏 Riding the Fences
  • P7771 【模板】欧拉路径

三、小结

图的有关算法到这里就告一段落了~

相关推荐
少司府2 小时前
C++基础入门:第一个C++程序
java·c语言·开发语言·c++·ide
Greg_Zhong2 小时前
Js中异步编程的知识扩展【异步有哪些、如何执行、宏任务和微任务等】
开发语言·javascript
黎阳之光2 小时前
黎阳之光:数智技术赋能水利“平急两用” 筑牢水利工程安全防线
大数据·人工智能·算法·安全·数字孪生
WG_172 小时前
Linux44+45:日志和线程池
算法
星辰_mya2 小时前
CGLIB 深度解剖:字节码生成的“克隆人”艺术
java·开发语言·面试
我命由我123452 小时前
React - 路由样式丢失问题、路由观察记录、路由传递参数
开发语言·前端·javascript·react.js·前端框架·html·ecmascript
️是782 小时前
信息奥赛一本通—编程启蒙(3345:【例60.2】 约瑟夫问题)
开发语言·c++·算法
LSL666_2 小时前
IService——查询(下)
java·开发语言·数据库·mybatisplus·iservice
众创岛2 小时前
python中enumerate的用法
开发语言·python