有向图Hierholzer算法的另一种实现

由于在有向图中,Hierholzer 算法不需要删除反向边,因此我们可以使用其它更加方便的数据结构来保存图。例如,我们可以使用邻接表**^8**保存有向边,得到有向图 Hierholzer 算法的另一种 C++ 代码实现。

复制代码
// e[i] 是一个类型为 vector<int> 的变量,保存了节点 i 与哪些节点之间有边相连
// ans 是一个类型为 vector<vector<int>> 的变量,用来存储我们找到的回路
// 回路用边的形式表示,一条边用有两个元素的 vector<int> 表示

void dfs(int sn) {
    while (e[sn].size() > 0) {
        int fn = e[sn].back();
        // 删除有向边 sn -> fn
        e[sn].pop_back();
        // 继续遍历相邻点
        dfs(fn);
        // 将边 sn -> fn 加入结果序列中
        ans.push_back({sn, fn});
    }
}

在上述实现中,我们每次遍历的是从 sn 出发,且未被删除的最后一条有向边 sn -> fn。之所以选择最后一条边,是因为我们可以通过 vector 的 pop_back 方法,在 的时间复杂度内删除这条边。因此上述实现的时间复杂度仍然是 的,且比链式前向星的实现方式更加简洁。

例题

我们仍然使用下面这道基础例题**^7**完整地展示 Hierholzer 算法在有向图中的使用。

直接使用当前弧优化的 Hierholzer 算法即可。本题的 C++ 代码如下。

复制代码
#include <bits/stdc++.h>
#define MAXN ((int) 1e5)
#define MAXM ((int) 2e5)
using namespace std;

int n, m;
vector<int> ans;

struct Edge {
    // 因为不需要删除反向边,我们直接把编号为 idx 的边保存在 e[idx] 里
    // 这样就不需要额外记一个 idx 了
    int fn, nxt;
    bool del;
} e[MAXM + 10];
int p[MAXN + 10];

int inDeg[MAXN + 10], outDeg[MAXN + 10];

// 加入第 idx 条有向边 x -> y
void adde(int x, int y, int idx) {
    e[idx] = Edge { y, p[x], false }; p[x] = idx;
}

void dfs(int sn) {
    // 当前弧优化的 Hierholzer 算法
    for (int i = p[sn]; i != 0; i = p[sn]) {
        if (e[i].del) {
            // 这条边已经被删除了,修改 p[sn] 指向它的下一条边
            p[sn] = e[i].nxt;
            continue;
        }
        // 删除有向边 e[i],不需要删除反向边
        e[i].del = true;
        // 继续遍历相邻点
        dfs(e[i].fn);
        // 将边 e[i] 的编号加入结果序列中
        ans.push_back(i);
    }
}

int main() {
    // 读入点数和边数
    scanf("%d%d", &n, &m);

    // 读入所有有向边
    for (int i = 1; i <= m; i++) {
        int x, y; scanf("%d%d", &x, &y);
        adde(x, y, i);
        outDeg[x]++; inDeg[y]++;
    }

    // 检查是否所有点的入度等于出度
    for (int i = 1; i <= n; i++) if (inDeg[i] != outDeg[i]) {
        printf("NO\n");
        return 0;
    }

    // 必须从一个非零度节点开始深度优先搜索
    for (int i = 1; i <= n; i++) if (inDeg[i] + outDeg[i] > 0) {
        dfs(i);
        break;
    }
    // ans 保存的是欧拉回路的倒序,必须 reverse 才是正确答案
    reverse(ans.begin(), ans.end());

    // 这里实际上是对原图弱连通性的检查
    // 如果原图的非零度节点不连通,那么 ans 里将不足 m 条边
    if (ans.size() != m) printf("NO\n");
    else {
        // 输出欧拉回路
        printf("YES\n");
        for (int i = 0; i < m; i++) printf("%d%c", ans[i], "\n "[i + 1 < m]);
    }
    return 0;
}
相关推荐
大明者省6 小时前
WSL2 Ubuntu22.04 GPU训练环境配置指南
人工智能·算法·计算机视觉
白狐_7987 小时前
408数据结构第8章:排序②——性质对比秒杀、场景选择与外部排序
java·数据结构·算法
zander2588 小时前
LeetCode 84:柱状图中的最大矩形——单调栈如何确定左右边界
java·数据结构·算法
Shell运维手记8 小时前
Linux 常用基础命令学习笔记
linux·运维·笔记·学习·算法·github
杨航 AI8 小时前
O(n log n):线性对数原理拆解 这个是排序算法的黄金复杂度之一。
算法·排序算法
船厂电气自动化ai大模型9 小时前
AI大模型与数学 第32课 函数凹凸性与二阶导数:拐点求解、凹凸区间计算(10道二阶导数计算题)
数据结构·人工智能·python·深度学习·算法
旖旎夜光9 小时前
LeetCode 3:无重复字符的最长子串(滑动窗口) —— 题解
数据结构·c++·算法·leetcode·滑动窗口
叠层归一研究院10 小时前
AGI 系统(十一):二阶网络 — 二阶递归 × 多主体 (元符号网络)
开发语言·人工智能·算法·php·agi
zlinear数据采集卡11 小时前
D223的PWM电机控制:6路独立脉冲+加减速算法深度解析
arm开发·stm32·嵌入式硬件·算法·fpga开发·架构
杨石兴11 小时前
31、玩具MoM:用2x2矩阵串起全套流程
人工智能·算法·矩阵·电磁学