备战蓝桥杯:树的存储与遍历(dfs和bfs)

news2025/1/15 3:53:43

树的概念

树的逻辑结构是树形结构,和我们之前的线性结构又不太一样了,是一种一对多的关系

树的结点分为根节点,叶子结点(没有分支的结点) 以及分支结点

从上往下看,每个结点都有0个或多个后继

从下往上看,每个结点除了根结点以外都有一个前驱结点

所以每个结点都有一条边去连接前驱结点,而根结点没有边连接前驱结点,所以结点数=边数+1(除了根结点每个结点都有一条边连接前驱结点,再加上根结点就是结点的个数)

如图,从下往上看,除了根结点,每个结点都有一个前驱结点,也就是对应一条边。所以结点数=边数(9)+1 = 10

关于树的一些相关术语

父结点:直接前驱,根结点没有父结点

孩子结点:直接后继,叶子结点没有直接后继

结点的度:该结点孩子的个数,比如上图A结点的度就是3

树的度:所有结点的度的最大值

树的高度:一共有多少层,图中有四层

两个结点之间的路径:两个结点之间的最短路径

路径长度:两点的路径中,边的个数

有序树:结点的子树顺序按从左到右有序,不能随意更改

无序树:结点的子树顺序是无序的,随便变,没事儿

我们竞赛一般用的都是无序树

有根树:根是固定的

无根树:根是不固定的

(无根树会导致父子关系不明确,我们要把所有清空都存储起来,比如假如a和b之间存在一条边,那我们既要把a存在b的孩子里,也要把b存在a的孩子里)

我们竞赛中用的都是无根树

树的存储

关于树的存储方式有很多,我们本篇文章只学孩子表示法,未来我们的并查集里会学到双亲表示法,竞赛里,我们只需要掌握这两种表示方法就ok了

孩子表示法:对于每个结点,把他所有的孩子全部存起来

因为我们竞赛都是无根树,我们需要把所有情况都考虑到

用vector数组实现孩子表示法

我们竞赛里,一般都是这样给信息

我们首先要创建一个大小充足 的vector的数组,把每个结点的孩子都各自存储在各自的vector里面

如图,我们九个结点,我们就创建一个大小为10的vector数组,下标为0的vector数组我们不用

#include <iostream>
#include <vector>
using namespace std;
const int N = 1e5 + 10;
vector <int> v1[N];
int main()
{
	int n;
	cin >> n;
	

}

比如我们看第一个输入的是3和1,那么3和1之间有一条边,我们竞赛都是无根树,所以我们把3当成1的孩子存一次,把1当成3的孩子存一次

1的孩子结点:2,3,4

2的孩子结点:1,5,6

3的孩子结点:1

4的孩子结点:1,7,8,9

5的孩子结点:2

6的孩子结点:2

7,8,9的孩子结点:4

存储时候的代码

#include <iostream>
#include <vector>
using namespace std;
const int N = 1e5 + 10;
vector <int> v1[N];
int main()
{
	int n;
	cin >> n;
	for (int i = 1; i < n; i++)
	{
		int x, y; cin >> x >> y;
		v1[x].push_back(y);
		v1[y].push_back(x);

	}

}

用链式前向星实现孩子表示法

链式前向星就是用链表来存储所有的孩子

我们需要创建一个数组来存储每个结点的一个哨兵位,

然后创建一个哨兵位数组2倍的数组来存储完成e[2*N]和ne[2*N]的数组,还要有id表示存储位置(这里是两倍,因为我们有的结点可能不止一个边,那么就有可能存储两次)

我们实现的时候就是用头插来实现的

比如这个图,我们3和1有一条边,那我们就要把3的哨兵位连接一个1,把1结点的哨兵位连接一个3,代码也就是:id++,e[id] = 1,ne[id]=h[3] h[3] = id,这是把1连接到3的哨兵位,3连接1同理,我们暂且不实现,3和4有一条边,我们把4连接到3的哨兵位的后面,同样是采取头插,id++,e[id]=4,ne[id]=h[3],h[3]=id,这样我们的3结点哨兵位就变成了 头->4->1了,也就是3的两个孩子

实现完整代码如下

#include <iostream>
#include <vector>
using namespace std;
const int N = 1e5 + 10;
int h[N], e[2 * N], ne[2 * N], id;
void add(int x, int y)
{
	id++;
	e[id] = x;
	ne[id] = h[y];
	h[y] = id;
}
int main()
{
	int n;
	cin >> n;
	for (int i = 1; i < n; i++)
	{
		int x, y; cin >> x >> y;
		add(x, y); add(y, x);
	}
}

树的遍历(DFS和BFS)

DFS,即深度优先遍历,英文叫做Depth First Search

是一种遍历树或者图的一种算法,所谓深度优先遍历,就是每次都尝试往更深的结点走

也就是一条路走到黑

从根结点开始,遍历根结点的孩子,然后再以这个孩子为根遍历它的孩子,所以我们可以写成一种递归的形式

vector存储的树进行dfs

#include <iostream>
#include <vector>
using namespace std;
const int N = 1e6 + 10;
vector <int> edges[N];
bool st[N];

void dfs(int u)
{
	cout << u << " ";
	st[u] = true;
	for (auto v : edges[u])
	{
		if (!st[v])
			dfs(v);
	}
}
int main()
{
	int n;
	cin >> n;
	//建树
	for (int i = 1; i < n; i++)
	{
		int a, b; cin >> a >> b;
		edges[a].push_back(b);
		edges[b].push_back(a);
	}
	//深度优先搜索
	dfs(1);



	return 0;
}

过程就是不断的遍历根结点的孩子,不断的遍历根结点的孩子,我们来画一下递归过程图

递归的展开图就是一棵树,递归就是对树进行深度搜索

为了更好的理解递归,我们再画一下斐波那契数列的递归展开图

可以看到,斐波那契算法的递归图画出来也是一棵树

链式前向星存储的树进行dfs

#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int h[N], e[2*N], ne[2*N], id;
bool st[N];
void add(int a, int b)
{
	id++;
	e[id] = b;
	ne[id] = h[a];
	h[a] = id;
}
void dfs(int u)
{
	cout << u << " ";
	st[u] = true;
	for (int v = h[u]; v; v = ne[v])
	{
		if (!st[e[v]])
		{
			dfs(e[v]);
		}
	}
}
int main()
{
	int n;
	cin >> n;
	for (int i = 1; i < n; i++)
	{
		int a, b; cin >> a >> b;
		add(a, b); add(b, a);
	}
	dfs(1);


	return 0;
}

vector存储的树进行bfs

bfs的话,我们的思路就是用队列,我们先把根结点入队列,然后出队列的时候要把该结点的孩子入队列,直到队列为空,搜索完毕

比如这个,我们先把1入队列,然后把输出1并把1出队列,把3,5,2入队列,然后把输出3并把3出队列,把3的孩子7,10带进去,

出5,把4带进去,出2把11带进去,这时候我们的队列里就是7,10,4,11了 我们输出了1,3,5,2 接下来也是这种操作,直到队列为空的时候我们遍历完毕

下面实现我们的代码

#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int N = 1e5 + 10;
queue <int> q;
vector <int> edges[N];
bool st[N];
void bfs()
{
	q.push(1); 

	while (q.size())
	{
		auto t = q.front();
		cout << t << " ";
		st[t] = true;
		q.pop();
		for (auto e : edges[t])
		{
			if (!st[e])
			{
				q.push(e);
				st[e] = true;
			}
		}
	}
	
}
int main()
{
	int n;
	cin >> n;
	for (int i = 1; i < n; i++)
	{
		int a, b; cin >> a >> b;
		edges[a].push_back(b);
		edges[b].push_back(a);
	}
	bfs();

}

符合我们的树,结束

链式前向星存储的树进行bfs

#include <iostream>
#include <queue>
using namespace std;

const int N = 1e5 + 10;
int h[N], ne[2 * N], e[2 * N], id;
bool st[N];
void add(int a,int b)
{
	id++;
	e[id] = b;
	ne[id] = h[a];
	h[a] = id;
}
queue <int> q;
void bfs()
{
	q.push(1);
	
	while (q.size())
	{
		auto t = q.front(); q.pop();
		cout << t << " ";
		st[t] = true;
		for (int i = h[t]; i; i = ne[i])
		{
			if (!st[e[i]])
			{
				q.push(e[i]);
				st[e[i]] = true;
			}
		}
	}
}


int main()
{
	int n;
	cin >> n;
	for (int i = 1; i < n; i++)
	{
		int a, b; cin >> a >> b;
		add(a, b); add(b, a);
	}
	bfs();


	return 0;
}

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

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

相关文章

超大规模分类(三):KNN softmax

传统的分类损失计算输入数据和每个类别中心的距离&#xff0c;来优化模型的训练。KNN softmax通过选择和输入数据最相关的top-K个类别&#xff0c;仅计算输入数据和top-K个类别中心的距离&#xff0c;以减小计算量。 KNN softmax首次诞生于达摩院机器智能技术实验室发表的SIGKD…

ubuntu官方软件包网站 字体设置

在https://ubuntu.pkgs.org/22.04/ubuntu-universe-amd64/xl2tpd_1.3.16-1_amd64.deb.html搜索找到需要的软件后&#xff0c;点击&#xff0c;下滑&#xff0c; 即可在Links和Download找到相关链接&#xff0c;下载即可&#xff0c; 但是找不到ros的安装包&#xff0c; 字体设…

项目实战——使用python脚本完成指定OTA或者其他功能的自动化断电上电测试

前言 在嵌入式设备的OTA场景测试和其他断电上电测试过程中&#xff0c;有的场景发生在夜晚或者随时可能发生&#xff0c;这个时候不可能24h人工盯着&#xff0c;需要自动化抓取串口日志处罚断电上电操作。 下面的python脚本可以实现自动抓取串口指定关键词&#xff0c;然后触发…

电脑分辨率调到为多少最佳?电脑分辨率最佳设置

电脑分辨率是指电脑屏幕上显示的像素点的数量&#xff0c;通常用水平和垂直方向的像素点数来表示&#xff0c;例如19201080。像素点越多&#xff0c;显示的内容就越清晰&#xff0c;但也会占用更多的系统资源和电力。那么多电脑分辨率多少最佳&#xff1f;以及电脑分辨率如何调…

代码随想录算法【Day20】

Day20 二叉搜索树 235. 二叉搜索树的最近公共祖先 理解只要当前节点的值在p和q节点的值的中间&#xff0c;那这个值就是最近的公共祖先&#xff0c;绝对不是次近的&#xff0c;这个题就好做了。 递归法 二叉搜索树本身是有序的&#xff0c;所以不涉及到前中后序的遍历 cl…

【SpringBoot】@Value 没有注入预期的值

问题复现 在装配对象成员属性时&#xff0c;我们常常会使用 Autowired 来装配。但是&#xff0c;有时候我们也使用 Value 进行装配。不过这两种注解使用风格不同&#xff0c;使用 Autowired 一般都不会设置属性值&#xff0c;而 Value 必须指定一个字符串值&#xff0c;因为其…

车联网安全 -- 数字证书到底证明了什么?

在车联网安全--TLS握手过程详解里面&#xff0c;我们了解到握手时&#xff0c;Server会向Client发送Server Certificate&#xff0c;用于证明自己的身份合法&#xff0c;为什么会有这一步呢&#xff1f; 我们回顾一下数字签名的过程&#xff1a; Bob将使用自己的公钥对“Hello…

Elasticsarch:使用全文搜索在 ES|QL 中进行过滤 - 8.17

8.17 在 ES|QL 中引入了 match 和 qstr 函数&#xff0c;可用于执行全文过滤。本文介绍了它们的作用、使用方法、与现有文本过滤方法的区别、当前的限制以及未来的改进。 ES|QL 现在包含全文函数&#xff0c;可用于使用文本查询过滤数据。我们将回顾可用的文本过滤方法&#xf…

【HTML+CSS+JS+VUE】web前端教程-31-css3新特性

圆角 div{width: 100px;height: 100px;background-color: saddlebrown;border-radius: 5px;}阴影 div{width: 200px;height: 100px;background-color: saddlebrown;margin: 0 auto;box-shadow: 10px 10px 20px rgba(0, 0, 0, 0.5);}

Spring Boot 项目自定义加解密实现配置文件的加密

在Spring Boot项目中&#xff0c; 可以结合Jasypt 快速实现对配置文件中的部分属性进行加密。 完整的介绍参照&#xff1a; Spring Boot Jasypt 实现application.yml 属性加密的快速示例 但是作为一个技术强迫症&#xff0c;总是想着从底层开始实现属性的加解密&#xff0c;…

若依前后端分离项目部署(使用docker)

文章目录 一、搭建后端1.1 搭建流程&#xff1a;1.2 后端零件:1.2.1 mysql容器创建&#xff1a;1.2.2 redis容器创建&#xff1a;1.2.3 Dockerfile内容&#xff1a;1.2.4 构建项目镜像&#xff1a;1.2.5 创建后端容器&#xff1a; 二、前端搭建&#xff1a;2.1 搭建流程&#x…

Vue2+OpenLayers使用Overlay实现点击获取当前经纬度信息(提供Gitee源码)

目录 一、案例截图 二、安装OpenLayers库 三、代码实现 关键参数&#xff1a; 实现思路&#xff1a; 核心代码&#xff1a; 完整代码&#xff1a; 四、Gitee源码 一、案例截图 二、安装OpenLayers库 npm install ol 三、代码实现 覆盖物&#xff08;Overlay&#xf…

Oracle 终止正在执行的SQL

目录 一. 背景二. 操作简介三. 投入数据四. 效果展示 一. 背景 项目中要求进行性能测试&#xff0c;需要向指定的表中投入几百万条数据。 在数据投入的过程中发现投入的数据不对&#xff0c;需要紧急停止SQL的执行。 二. 操作简介 &#x1f449;需要DBA权限&#x1f448; ⏹…

Oopsie【hack the box】

Oopsie 解题流程 文件上传 首先开启机器后&#xff0c;我们先使用 nmap -sC -SV来扫描一下IP地址&#xff1a; -sC&#xff1a;使用 Nmap 的默认脚本扫描&#xff08;通常是 NSE 脚本&#xff0c;Nmap Scripting Engine&#xff09;。这个选项会自动执行一系列常见的脚本&am…

V少JS基础班之第四弹

一、 前言 第四弹内容是操作符。 本章结束。第一个月的内容就完成了&#xff0c; 是一个节点。 下个月我们就要开始函数的学习了。 我们学习完函数之后。很多概念就可以跟大家补充说明了。 OK&#xff0c;那我们就开始本周的操作符学习 本系列为一周一更&#xff0c;计划历时6…

【STM32-学习笔记-7-】USART串口通信

文章目录 USART串口通信Ⅰ、硬件电路Ⅱ、常见的电平标准Ⅲ、串口参数及时序Ⅳ、STM32的USART简介数据帧起始位侦测数据采样波特率发生器 Ⅴ、USART函数介绍Ⅵ、USART_InitTypeDef结构体参数1、USART_BaudRate2、USART_WordLength3、USART_StopBits4、USART_Parity5、USART_Mode…

Docker 安装开源的IT资产管理系统Snipe-IT

一、安装 1、创建docker-compose.yaml version: 3services:snipeit:container_name: snipeitimage: snipe/snipe-it:v6.1.2restart: alwaysports:- "8000:80"volumes:- ./logs:/var/www/html/storage/logsdepends_on:- mysqlenv_file:- .env.dockernetworks:- snip…

达梦8-DMSQL程序设计学习笔记1-DMSQL程序简介

1、DMSQL程序简介 DMSQL程序是达梦数据库对标准SQL语言的扩展&#xff0c;是一种过程化SQL语言。在DMSQL程序中&#xff0c;包括一整套数据类型、条件结构、循环结构和异常处理结构等&#xff0c;DMSQL程序中可以执行SQL语句&#xff0c;SQL语句中也可以使用DMSQL函数。 DMSQ…

NLP中常见的分词算法(BPE、WordPiece、Unigram、SentencePiece)

文章目录 一、基本概念二、传统分词方法2.1 古典分词方法2.2 拆分为单个字符 三、基于子词的分词方法&#xff08;Subword Tokenization&#xff09;3.1 主要思想3.2 主流的 Subword 算法3.3 Subword 与 传统分词方法的比较 四、Byte Pair Encoding (BPE)4.1 主要思想4.2 算法过…

第三十六章 Spring之假如让你来写MVC——拦截器篇

Spring源码阅读目录 第一部分——IOC篇 第一章 Spring之最熟悉的陌生人——IOC 第二章 Spring之假如让你来写IOC容器——加载资源篇 第三章 Spring之假如让你来写IOC容器——解析配置文件篇 第四章 Spring之假如让你来写IOC容器——XML配置文件篇 第五章 Spring之假如让你来写…