欧拉回路与哈密尔顿回路: Fleury算法与Hierholzer 算法(C++)

news2025/2/26 6:15:00

图论中的回路是指一个路径, 它从某个顶点开始, 经过所有边恰好一次, 并回到起始顶点.

定义

  • 欧拉回路: 从一个顶点出发, 经过每条边恰好一次, 并且最终回到起始顶点.

  • 哈密尔顿回路: 从一个顶点出发, 经过每个顶点恰好一次, 并且最终回到起始顶点.

  • 欧拉路径: 从一个顶点出发, 访问图中的每一个边恰好一次, 但不需要回到起始顶点.

  • 哈密尔顿路径: 从一个顶点出发, 访问图中的每一个其他顶点恰好一次, 但不需要回到起始顶点.

sample

欧拉回路

无向图的条件

  • 对于无向图, 构成欧拉回路的充要条件是: 所有顶点的度数都必须是偶数.
  • 如果仅有两个顶点的度数为奇数, 则存在从其中一个顶点到另一个顶点的欧拉路径, 但不是欧拉回路.

柯尼斯堡

欧拉证明七桥问题没有解, 因为存在度为奇数的顶点.

有向图的条件

  • 对于有向图, 每个顶点的入度必须等于出度才能构成欧拉回路.
  • 如果仅有一个顶点的出度比入度多 1, 且另一个顶点的入度比出度多 1, 其余顶点的出入度相等, 则存在从出度多 1 的顶点到入度多 1 的顶点的欧拉路径.

求解算法

求解欧拉回路的主要算法包括 Fleury 算法和 Hierholzer 算法:

Fleury 算法解析

Fleury 算法是一种较为直观的方法, 逐步构造欧拉回路, 但其效率较低, 因为需要检查每一步是否会破坏图的连通性.

算法步骤如下:

  1. 选择起点:

    • 如果图中存在欧拉回路, 则可以从任意顶点开始.
    • 如果图中只存在欧拉路径, 则必须从度数为奇数的两个顶点之一开始.
  2. 遍历边:

    • 从当前顶点出发, 选择下一条边进行遍历.
    • 除非没有其他选择, 不然需要避免选择"桥"(或者说割边). 判断某条边是否为桥梁可以通过暂时移除该边并检查图是否仍然连通来实现. 加入断开了这条边之后, 原先的图不再相连, 则此边是一个桥.
  3. 标记已访问的边: 每次选择一条边后, 将其标记为已访问, 并将其从图中移除(或者记录下来以便后续恢复).

  4. 移动到下一个顶点: 移动到所选边的另一端点, 并重复上述过程, 直到所有边都被访问过.

  5. 返回起点:

    • 如果是从欧拉回路的起点开始, 则最终会回到该起点, 形成一个闭合回路.
    • 如果是从欧拉路径的一个端点开始, 则最终会到达另一个端点, 形成一条欧拉路径.
示例

求下图的欧拉路径:

sample

fleury 算法步骤:

  1. 任意选定起点, 假定选择了A., A有两条边, 均不是桥, 任选一个都可以. 假定我们选择了A-B. 此时结果如下:

    f1

  2. 此时我们到达了B, B的的三条边均不是桥, 任选其一. 假定选了B-E. 结果如下:

    f2

  3. 此时我们到达了E, 三条边均可选 假定选了E-D. 结果如下:

    f3

  4. 现在到达了D, 注意D-A是一个桥, 因为此时还有其他边可选, 所不能选D-A.

    f4

  5. 后续步骤不再赘述, 看 gif.

    fig

Hierholzer 算法

Hierholzer 算法是一个更为高效的方法, 通过利用回路合并的思想来构建欧拉回路. 它的基本思想是从任意一个顶点开始, 尝试访问每一条边, 并将访问过的边移除, 直到无法继续前进时, 再回溯寻找新的未访问边, 直到所有的边都被访问过为止.

算法步骤
  1. 选择起点:

    • 从任意一个顶点开始(对于欧拉回路, 任何顶点都可以作为起点; 对于欧拉路径, 则需要从度数为奇数的顶点之一开始).
  2. 初始化路径:

    • 创建一个空列表 path 来存储当前找到的路径.
    • 创建一个栈 stack 并将起点压入栈中.
  3. 遍历边:

    • 当栈不为空时, 执行以下操作:
      1. 弹出栈顶元素: 将栈顶元素取出并设为当前顶点 current_vertex.
      2. 检查相邻边: 检查当前顶点的所有未访问过的相邻边.
      3. 如果存在未访问的边:
        • 随机选择一条未访问的边, 并将其标记为已访问.
        • 将该边的另一端点推入栈中.
      4. 如果没有未访问的边:
        • 将当前顶点添加到 path 列表中.
  4. 合并路径:

    • 当栈为空时, path 列表中的顶点顺序即为所求的欧拉回路. 但由于我们是从后往前添加顶点的, 因此需要反转 path 列表.
  5. 返回结果:

    • 返回反转后的 path 列表, 这就是所求的欧拉回路.
示例演示

针对上题中提到的样例, hierholzer 的步骤如下图所示:

h

需要注意的是:

  1. A被第二次访问的时候, 此时没有其他边可走, 因此需要从栈中弹出A并添加到path中.
  2. 接下来的出栈操作在所有节点访问完毕的时候.
代码实现

以下是一个具体的 C++ 实现的核心部分, 完整代码请参考:

std::vector<int> FindEulerCircuit(int start) {
  std::stack<int> stack;  // 当前路径
  std::vector<int> path;  // 存储最终的欧拉回路

  stack.push(start);

  while (!stack.empty()) {
    int currV = stack.top();

    // 如果当前顶点有未访问的边
    auto adjList = graph_.Adj(currV);
    if (!adjList.empty()) {
      int nextV = *adjList.begin();
      graph_.RemoveEdge(currV, nextV);
      stack.push(nextV);
    } else {
      // 如果没有未访问的边,则将当前顶点加入电路
      path.push_back(currV);
      stack.pop();
    }
  }

  // 反转电路以获得正确的顺序
  std::reverse(path.begin(), path.end());
  return path;
}

完整代码请参考: Hierholzer.ixx

时间复杂度

Hierholzer 算法的时间复杂度为 O ( E ) O(E) O(E), 其中 E E E 是图中的边数. 这是因为每条边只会被访问一次, 并且在每次访问时只需要常数时间的操作(如栈操作和边的删除). 这使得 Hierholzer 算法在处理大规模图时非常高效.

汉密尔顿回路

寻找一个给定图是否存在哈密尔顿回路的问题是一个典型的 NP 完全问题, 这意味着目前没有已知的有效算法可以在多项式时间内解决任意图的这个问题. 通常采用的方法包括暴力搜索, 回溯法以及一些启发式的优化策略来尝试解决特定实例的问题.

由于其复杂性, 对于较大的图, 求解哈密尔顿回路往往需要消耗大量的计算资源. 然而, 在某些特殊情况下, 如图具有特定结构时, 可以设计出有效的算法来解决问题. 例如, 在竞赛编程或者算法面试中, 如果图的规模较小(比如不超过 30 个顶点), 可以通过状态压缩动态规划等方法来尝试解决.

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

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

相关文章

20250221 NLP

1.向量和嵌入 https://zhuanlan.zhihu.com/p/634237861 encoder的输入就是向量&#xff0c;提前嵌入为向量 二.多模态文本嵌入向量过程 1.文本预处理 文本tokenizer之前需要预处理吗&#xff1f; 是的&#xff0c;文本tokenizer之前通常需要对文本进行预处理。预处理步骤可…

https:原理

目录 1.数据的加密 1.1对称加密 1.2非对称加密 2.数据指纹 2.1数据指纹实际的应用 3.数据加密的方式 3.1只使用对称加密 3.2只使用非对称加密 3.3双方都使用对称加密 3.4非对称加密和对称加密一起使用 4.中间人攻击 5.CA证书 5.1什么是CA证书 CA证书的验证 6.https的原理 1.数据…

数据驱动未来!天合光能与永洪科技携手开启数字化新篇章

在信息化时代的今天&#xff0c;企业间的竞争早就超越了传统产品与服务的范畴&#xff0c;新的核心竞争力即——数据处理能力和信息技术的应用。作为数据技术领域的领军者&#xff0c;永洪科技凭借其深厚的技术积累和丰富的行业经验&#xff0c;成功助力天合光能实现数字化升级…

JavaScript数据结构-模拟链表

在JavaScript中没有链表这种数据结构&#xff0c;但是我们可以用对象(Object)模拟链表&#xff0c;下面让我们先了解链表是什么。 链表&#xff08;Linked List&#xff09;是一种基础的数据结构&#xff0c;由一系列节点&#xff08;Node&#xff09;组成&#xff0c;每一个节…

tableau之网络图和弧线图

一、网络图 概念 网络图&#xff08;Network Graph&#xff09;&#xff0c;也称为网络可视化&#xff0c;是数据可视化的一种形式&#xff0c;用于显示实体&#xff08;节点&#xff09;之间的关系&#xff08;边&#xff09;。这种图表通过节点和边的结构揭示数据中的复杂关…

Linux网络数据包接收:原理、流程与优化策略

在当今数字化时代&#xff0c;网络已成为计算机系统不可或缺的部分。无论是日常的网页浏览、文件传输&#xff0c;还是大规模数据中心的高效通信&#xff0c;网络数据包的收发都在其中扮演着重要角色。对于 Linux 系统而言&#xff0c;深入理解网络数据包的接收过程&#xff0c…

sklearn中的决策树-分类树:实例-分类树在合成数据集上的表现

分类树实例&#xff1a;分类树在合成数据集上的表现 代码分解 在不同结构的据集上测试一下决策树的效果&#xff08;二分型&#xff0c;月亮形&#xff0c;环形&#xff09; 导入 import numpy as np from matplotlib import pyplot as plt from matplotlib.colors import Li…

给小米/红米手机root(工具基本为官方工具)——KernelSU篇

目录 前言准备工作下载刷机包xiaomirom下载刷机包【适用于MIUI和hyperOS】“hyper更新”微信小程序【只适用于hyperOS】 下载KernelSU刷机所需程序和驱动文件 开始刷机设置手机第一种刷机方式【KMI】推荐提取boot或init_boot分区 第二种刷机方式【GKI】不推荐 结语 前言 刷机需…

棒球和垒球区别·棒球1号位

棒球运动和垒球运动的区别主要体现在以下几个方面&#xff1a; 1. 用球差异&#xff1a;垒球比棒球大且重。棒球的直径大约是7.3厘米&#xff0c;重量通常在145克左右&#xff0c;外皮由皮革制成&#xff0c;质地较硬。而垒球的直径为9.7厘米&#xff0c;重量大约为180克左右&a…

Redis|持久化

文章目录 总体介绍RDB&#xff08;Redis DataBase&#xff09;官网介绍案例演示优势劣势如何检查修复 dump.rdb 文件哪些情况下会触发 RDB 快照如何禁用快照RDB 优化配置项详解小总结 AOF&#xff08;Append Only File&#xff09;官网介绍是什么能干嘛AOF 持久化工作流程AOF 缓…

Windows前端开发IDE选型全攻略

Windows前端开发IDE选型全攻略 一、核心IDE对比矩阵 工具名称最新版本核心优势适用场景推荐指数引用来源VS Code2.3.5轻量级/海量插件/跨平台/Git深度集成全栈开发/中小型项目⭐⭐⭐⭐⭐14WebStorm2025.1智能提示/框架深度支持/企业级调试工具大型项目/专业前端团队⭐⭐⭐⭐47…

基于Spring Boot的党员学习交流平台设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

【2025-02-25】基础算法:二分查找(一)

&#x1f4dd;前言说明&#xff1a; ●本专栏主要记录本人的基础算法学习以及LeetCode刷题记录&#xff0c;主要跟随B站博主灵茶山的视频进行学习&#xff0c;专栏中的每一篇文章对应B站博主灵茶山的一个视频 ●题目主要为B站视频内涉及的题目以及B站视频中提到的“课后作业”。…

如何在 SpringBoot 项目使用 Redis 的 Pipeline 功能

本文是博主在批量存储聊天中用户状态和登陆信息到 Redis 缓存中时&#xff0c;使用到了 Pipeline 功能&#xff0c;并对此做出了整理。 一、Redis Pipeline 是什么 Redis 的 Pipeline 功能可以显著提升 Redis 操作的性能&#xff0c;性能提升的原因在于可以批量执行命令。当我…

2024年第十五届蓝桥杯青少 图形化编程(Scratch)省赛中级组真题——截取递增数

截取递增数 背景信息 递增数&#xff1a;如果一个大于9的正整数各个数位上的数&#xff0c;从左到右是逐渐变大的&#xff0c;那么就称这个数为递增数。 例如124、248 是递增数。 给你一个不含0的九位数&#xff0c;请找出从这个九位数中能截取出的所有递增数。例如:115367…

【ECMAScript6】

【ECMAScript6】 01. ES6介绍02. let和const命令03. 模板字符串04. 函数之默认值、剩余参数05. 函数之扩展运算符、箭头函数06. 箭头函数this指向和注意事项07. 解构赋值08. 扩展的对象的功能&#xff08;简写&#xff09;09. Symbol类型10. Set集合数据类型11. Map数据类型12.…

WebUI 部署 Ollama 可视化对话界面

文章目录 一、Node.js 安装1.系统环境查询2.官网下载nodejs 安装包3.安装 Node.js 并配置环境变量4.验证安装是否正确 二、ollama-webui 安装与配置1.代码库下载2.依赖安装3.运行 三、遇到问题与解决 一、Node.js 安装 1.系统环境查询 ubuntu20.04 系统&#xff0c;x86-64架构…

BMS应用软件开发 — 17 上下电控制与诊断开发 (Simulink)

目录 17.1 上下电控制流程 17.1.1 上下电流程 17.1.2 下电过程的电机放电 17.1.3 继电器状态检测 17.2 预充继电器状态判断 17.1 上下电控制流程 17.1.1 上下电流程 高压上电是指动力电池为车辆提供高压&#xff0c;使高压回路导通&#xff0c;为车辆的各个高压部件供电&…

使用Open WebUI下载的模型文件(Model)默认存放在哪里?

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;Ollama部署LLM专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2025年2月21日21点21分 &#x1f004;️文章质量&#xff1a;95分 文章目录 使用CMD安装存放位置 默认存放路径 Open WebUI下…

005:Cesium.viewer 知识详解、示例代码

查看本专栏目录 - 本文是第 005个API内容详解 vue+cesium 示例教程200+目录 文章目录 一、Cesium.Viewer 知识详解1. 主要用途2. 构造函数与参数3. 常用属性(1)`viewer.scene`(2)`viewer.camera`(3)`viewer.entities`(4)`viewer.clock`4. 常用方法(1)`viewer.zoomTo(…