DFS序 欧拉序

【算法分析】
● DFS 序

DFS 序表示从根结点开始对树进行 DFS 所得的结点遍历顺序

易得上图的 DFS 序为:1,2,3,4,5,6,7,8,9。可见,通过 DFS 序,可将一棵树映射为一个一维数组
假设以某结点 u 为根的子树大小为 cnt[u],u 在整棵树中的 DFS 序为 dfs[u],则可得结点 u 的所有子树对应的 DFS 序区间为
[dfs[u],dfs[u]+cnt[u]-1]。 → 这条性质是编写本题代码的关键。
容易发现,
一棵子树所有结点的 DFS 序是整棵树的 DFS 序的连续一段。例如,上图中以 d 为根的子树,它的各结点的 DFS 序 3,4,5,6 是整棵树的 DFS 序 1,2,3,4,5,6,7,8,9 中的连续一段。可见,借助DFS序,可以快速判断一个结点是否在某子树内对某子树进行操作,相当于在对应的 DFS 序区间内进行操作
利用链式前向星存图,求树的 DFS 序的代码如下:

void dfs_seq(int u,int v) { //dfs sequence
    dfs[u]=++id;
    cnt[u]=1;
    for(int i=h[u]; ~i; i=ne[i]) {
        int j=e[i];
        if(j==v) continue;
        dfs_seq(j,u);
        cnt[u]+=cnt[j];
    }
    return ;
}

● 欧拉序
欧拉序与 DFS 序很像,但也有不同。欧拉序表示
从根结点出发,按照 DFS 经过所有结点并返回根结点所得的结点遍历顺序(即返回时也要记录)。
若设
first[u] 是欧拉序中某结点 u 第一次出现的位置,last[u] 是欧拉序中 u 最后一次出现的位置,则以结点 u 为根的子树包含的结点,是欧拉序区间 [first[u], last[u]] 之间的所有结点。换句话说,就是 [first[u], last[u]] 之间的结点为 u 的子结点。
欧拉序有两种情况:
(1)DFS 时,第一次访问某结点时记录一次,随后每访问完该结点的一棵子树就再记录一次,一共有
2n-1 个编号。其中,n 是树中结点的个数。

易得上图的欧拉序为:1,2,3,4,3,5,3,6,3,2,7,2,1,8,9,8,1。这是第一种情况下的欧拉序。
针对第一种情况的欧拉序,具有如下性质:即若设
first[u] 是欧拉序中某结点 u 第一次出现的位置,first[v] 是欧拉序中某结点 v 第一次出现的位置,树上两结点 u, v 的最近公共祖先(LCA),为欧拉序区间 [first[u], first[v]][first[v], first[u]] 中时间戳最小的结点。其中,某结点的时间戳可以理解为第一次 DFS 遍历到该结点的顺序。  ------→ 再次特别提醒,此性质仅适用于第一种情况下的欧拉序
利用链式前向星存图,在第一种情况下,求树的欧拉序的代码如下:

void ola_seq1(int u,int v) { //ola sequence 1
    dfs[++id]=u;
    cnt[u]=1;
    for(int i=h[u]; ~i; i=ne[i]) {
        int j=e[i];
        if(j==v) continue;
        ola_seq1(j,u);
        dfs[++id]=u;
        cnt[u]+=cnt[j];
    }
    return ;
}

(2)DFS 时,每个结点入栈与出栈时分别记录一次,共 2n 个编号。其中,n 是树中结点的个数。

易得上图的欧拉序为:1,2,3,4,4,5,5,6,6,3,7,7,2,8,9,9,8,1。这是第二种情况下的欧拉序。
利用链式前向星存图,在第二种情况下,求树的欧拉序的代码如下:

void ola_seq2(int u,int v) { //ola sequence 2
    dfs[++id]=u;
    cnt[u]=1;
    for(int i=h[u]; ~i; i=ne[i]) {
        int j=e[i];
        if(j==v) continue;
        ola_seq2(j,u);        
        cnt[u]+=cnt[j];
    }
    dfs[++id]=u;
    return;
}

● 链式前向星:https://blog.csdn.net/hnjzsyjyj/article/details/139369904
val[idx]:存储编号为 idx 的边的值
e[idx]:存储编号为 idx 的结点的值
ne[idx]:存储编号为 idx 的结点指向的结点的编号
h[a]:存储头结点 a 指向的结点的编号


【算法代码】
下面代码的功能为输入 n 个结点,然后输入 n 对整数对 a 和 b 表示 a 和 b 之间有连边。如果 b 是 -1,那么 a 就是树的根。之后,分别按树的 DFS 序及两种欧拉序访问、输出对应的结点值。
由于本题采用链式前向星存树,而链式前向星是典型的
头插法构建方法,故输入与输出是逆序的。运行代码后,易见本题的输出与上文的图示相比,明显是逆序的。

#include <bits/stdc++.h>
using namespace std;

const int N=1e3+5;
const int M=N<<1;
int root;
int dfs[N],cnt[N],id;
int h[N],e[M],ne[M],idx;
int n,m;

void add(int a,int b) {
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void dfs_seq(int u,int v) { //dfs sequence
    dfs[u]=++id;
    cout<<u<<" ";
    cnt[u]=1;
    for(int i=h[u]; ~i; i=ne[i]) {
        int j=e[i];
        if(j==v) continue;
        dfs_seq(j,u);
        cnt[u]+=cnt[j];
    }
    return ;
}

void ola_seq1(int u,int v) { //ola sequence 1
    dfs[++id]=u;
    cout<<u<<" ";
    cnt[u]=1;
    for(int i=h[u]; ~i; i=ne[i]) {
        int j=e[i];
        if(j==v) continue;
        ola_seq1(j,u);
        dfs[++id]=u;
        cout<<u<<" ";
        cnt[u]+=cnt[j];
    }
    return;
}

void ola_seq2(int u,int v) { //ola sequence 2
    dfs[++id]=u;
    cout<<u<<" ";
    cnt[u]=1;
    for(int i=h[u]; ~i; i=ne[i]) {
        int j=e[i];
        if(j==v) continue;
        ola_seq2(j,u);
        cnt[u]+=cnt[j];
    }
    dfs[++id]=u;
    cout<<u<<" ";
    return;
}

int main() {
    cin>>n;
    memset(h,-1,sizeof(h));
    int a,b;
    for(int i=1; i<=n; i++) {
        cin>>a>>b;
        if(b==-1) root=a;
        else add(a,b),add(b,a);
    }

    id=0,dfs_seq(root,-1),cout<<endl;
    id=0,ola_seq1(root,-1),cout<<endl;
    id=0,ola_seq2(root,-1),cout<<endl;

    return 0;
}

/*
in:
9
1 -1
1 2
1 8
2 3
2 7
3 4
3 5
3 6
8 9

out:
1 8 9 2 7 3 6 5 4
1 8 9 8 1 2 7 2 3 6 3 5 3 4 3 2 1
1 8 9 9 8 2 7 7 3 6 6 5 5 4 4 3 2 1
*/





【参考文献】
https://www.cnblogs.com/stxy-ferryman/p/7741970.html
https://www.cnblogs.com/hetailang/p/16209936.html
https://blog.csdn.net/littlegengjie/article/details/134430600
https://blog.csdn.net/zht2002/article/details/128686361
 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Nginx+Tomcat负载均衡、动静分离群集方案

一、Tomcat简介 在现代 Web 服务架构中&#xff0c;Tomcat 和 Nginx 是两个至关重要的组件&#xff0c;负责处理用户请求并实现高性能的服务。本篇博客将深入探讨这些技术的原理和部署配置方法。 最初是由Sun的软件构架师詹姆斯邓肯戴维森开发。安装Tomcat后&#xff0c;安装…

最新区块链论文速读--CCF A会议 ICSE 2024 共13篇 附pdf下载 (2/2)

Conference&#xff1a;International Conference on Software Engineering (ICSE) CCF level&#xff1a;CCF A Categories&#xff1a;Software Engineering/System Software/Programming Languages Year&#xff1a;2024 Num&#xff1a;13 第1~7篇区块链文章请点击此处…

后端返回前端时间格式化

时间格式化的方法总共包含以下 5 种。 1.前端时间格式化 JS 版时间格式化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function dateFormat(fmt, date) { let ret; const opt { "Y": date.getFullYear().toString(), // 年 …

[Shell编程学习路线]——探讨Shell中变量的作用范围(export)

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f6e0;️Shell编程专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年6月14日10点14分 &#x1f004;️文章质量&#xff1a;95分 文章目录 ————前言———— 定义变量&#xff1a; 输出变…

[C][数据结构][排序][下][快速排序][归并排序]详细讲解

文章目录 1.快速排序1.基本思想2.hoare版本3.挖坑法4.前后指针版本5.非递归版本改写 2.归并排序 1.快速排序 1.基本思想 任取待排序元素序列的某元素作为基准值&#xff0c;按照该排序码将待排序集合分割成两子序列&#xff0c;左子序列中所有元素均小于基准值&#xff0c;右…

3389端口修改工具,3389端口修改工具的操作步骤

3389端口修改器&#xff1a; 这是一个专门用于修改3389端口的工具&#xff0c;可以方便地修改Windows远程桌面服务的端口号 使用注册表编辑器手动修改&#xff1a; 虽然这不是一个专门的工具&#xff0c;但Windows的注册表编辑器也可以用来修改3389端口。用户需要定位到特定的注…

雷军-2022.8小米创业思考-10-高效率模型:便宜有好货;产品好,价格厚道,公司盈利;爆品模式,分摊成本;资金库存快速周转;铁人三项,硬件,新零售,互联网

第十章 高效率模型 小米方法论 “铁人三项”的商业模式 完整的“小米模式”。这种模式有很多反直觉的地方&#xff0c;需要跟“便宜无好货”等很多固有观念做斗争。有些讽刺的是&#xff0c;小米模式天生就是为实现“便宜有好货”而奋斗。 效率是小米模式的基石&#xff0c…

【CT】LeetCode手撕—5. 最长回文子串

目录 题目1-思路2- 实现⭐5. 最长回文子串——题解思路 3- ACM实现 题目 原题连接&#xff1a;5. 最长回文子串 1-思路 子串的定义&#xff1a;子串是原始字符串的一个连续部分子序列的定义&#xff1a;子序列是原始字符串的一个子集记录最长回文子串的起始位置以及其长度&am…

我的创作纪念日(1825天)

Ⅰ、机缘 1. 记得是大一、大二的时候就听学校的大牛说&#xff0c;可以通过写 CSDN 博客&#xff0c;来提升自己的代码和逻辑能力&#xff0c;虽然即将到了写作的第六个年头&#xff0c;但感觉这句话依旧受用; 2、今年一整年的创作都没有停止&#xff0c;本年度几乎是每周都来…

Python基础教程(十七):CGI编程

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

轻兔推荐 —— Obsidian

via&#xff1a;轻兔推荐 - https://app.lighttools.net/ 简介 Obsidian 是一个强大的知识管理和笔记应用程序&#xff0c;它基于本地文件存储&#xff0c;支持Markdown格式&#xff0c;并提供丰富的插件生态系统。 - 通过双向链接和图谱视图&#xff0c;帮助用户发现笔记之间…

联动联调,科学调度——探索智慧水务(中水)管理平台的无人值守新路径!

项目背景 随着中国城市化的进程、城市规模以及对应的城市人口数量的增长&#xff0c;社会生产生活过程中产生的污水问题日益严重。如何实现污水再生、变废为宝显得尤为重要。 近年来&#xff0c;某市不断拓展与探索城市中水利用&#xff0c;让经无害化处理后的中水&#xff0…

ubuntu gitlab 部署 私有git库

我的版本 ubuntu-22.04.2-live-server-amd64 GitLab 社区版 v17.0.1 注意剩余硬盘需要3GB以上 一、更新软件 sudo apt update二、gitLab 需要一些依赖项才能正常运行 sudo apt install -y curl openssh-server ca-certificates postfix1、出现邮件 选择 “Internet Site”并…

华为wlan实验

分为三步&#xff1a;1、网络互通&#xff0c;2、AP上线&#xff0c;3、wlan业务 1、网络互通 crow-sw: vlan batch 20 100 dhcp enable int vlan 20 ip add 192.168.20.1 24 dhcp select interfaceinterface GigabitEthernet0/0/2port link-type accessport default vlan 100…

Python | Leetcode Python题解之第150题逆波兰表达式求值

题目&#xff1a; 题解&#xff1a; class Solution:def evalRPN(self, tokens: List[str]) -> int:op_to_binary_fn {"": add,"-": sub,"*": mul,"/": lambda x, y: int(x / y), # 需要注意 python 中负数除法的表现与题目不一…

单链表经典算法题 1

前言 学习了单链表&#xff0c;我们就做一些题来巩固一下。还有就是解题方法不唯一&#xff0c;我就只讲述为自己的方法。 目录 前言 1.移除链表元素 思路 代码 2.反转链表 思路 代码 3.链表的中间节点 思路 代码 总结 1.移除链表元素 思路 我们创建一个新的表…

直播预约:存内计算加速大模型-未来智能计算的新引擎

直播简介: 在人工智能飞速发展的今天&#xff0c;大模型的训练和推理对计算资源的需求日益增长。传统计算架构已逐渐难以满足其对速度和效率的极致追求。本次直播&#xff0c;我们将深入探讨如何利用存内计算技术&#xff0c;为大模型带来革命性的加速效果。 直播亮点: 技术…

易趋(EasyTrack)资深咨询顾问刘苗受邀为第十三届中国PMO大会演讲嘉宾

全国PMO专业人士年度盛会 易趋&#xff08;EasyTrack&#xff09;资深咨询顾问刘苗女士受邀为PMO评论主办的2024第十三届中国PMO大会演讲嘉宾&#xff0c;演讲议题为“企业级项目管理平台推动 IPD 数字化”。大会将于6月29-30日在北京举办&#xff0c;敬请关注&#xff01; 议…

开源AGV调度系统OpenTCS中的路由器(router)详解

OpenTCS中的任务分派器router详解 1. 引言2. 路由器(router)2.1 代价计算函数&#xff08;Cost functions&#xff09;2.2 2.1 Routing groups2.1 默认的停车位置选择2.2 可选停车位置属性2.3 默认的充电位置选择2.4 即时运输订单分配 3. 默认任务分派器的配置项4. 参考资料与源…

SpringBoot3 整合 Mybatis 完整版

本文记录一下完整的 SpringBoot3 整合 Mybatis 的步骤。 只要按照本步骤来操作&#xff0c;整合完成后就可以正常使用。1. 添加数据库驱动依赖 以 MySQL 为例。 当不指定 依赖版本的时候&#xff0c;会 由 springboot 自动管理。 <dependency><groupId>com.mysql&l…