当前位置: 首页 > news >正文

数据结构进阶:树与递归之美

树是一个对于我这种小白来说是接触的第一个较复杂的数据结构,不像之前的线性结构,树让人感觉是从一个线到面的进阶。

树的定义是由一个根节点和许多子节点组成,再由子节点成为新的根节点有点像递归的过程,因此树的许多操作都要有递归的参与。

树的基本术语:

节点的度:树中的节点的子节点的个数称为度。

树的度:树中节点最大的度。

树的高度:树的层数或者深度。

路径:两个子结点之间的距离。

树又分为有根树和无根树,无序树指的是树的根是变化的根节点可以是子节点,子节点可以是根节点。有根树的根节点是固定的。树又分为有序树和无序树,有序树中树的子节点不可变化,无序树反之。

树的储存是一个相较于线性结构完全不同的,由于一对多的特性,使得他的存储变得困难。当我们在处理无根树时,由于根的不确定性,所以应在每个节点相互存储两次。对此我们有两种存储方式;vector数组和链式前向星。

vector数组是将以根节点为数组名的数组中存储他的子节点。

#include <iostream> #include <vector> using namespace std; const int N = 1e5 + 10; int n;//节点的个数 vector<int>edges[N]; int main() { cin >> n; for(int i = 1; i < n; i++) { int u, v; cin >> u >> v; edges[u].push_back(v); edges[v].push_back(u);//由于没有固定的根节点,需要相互储存 } return 0; }

链式前向向星指的是用链表进行存储。

#include <iostream> using namespace std; const int N = 1e5 + 10; int h[N], e[2 * N], ne[2 * N]; int n, id; void add(int a, int b) { id++; e[id] = b; ne[id] = h[a]; h[a] = id; } int main() { cin >> n; for(int i; i < n; i++) { int a, b; cin >> a >> b; add(a,b); add(b,a);//要将两种根的情况存储 } return 0; }

树的遍历如果按照之前的方法随便遍历的话,很容易漏掉数据。因此树有它特有的两种遍历方式,深度优先遍历DFS和宽度优先遍历BFS。

深度优先遍历是由根节点为起点一直往子节点的子节点不断遍历,直到找到叶子节点(没有子节点)时,原路返回至其他子节点再进行遍历,直到将所有数据遍历完结束。

#include <iostream> #include <vector> using namespace std; const int N = 1e6 + 10; vector<int>edges[N]; int 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 u, v; cin >> u >> v; edges[u].push_back(v); edges[v].push_back(u); } dfs(1);//以1为根结点的树 }

上述使用的是vector数组储存的树的深度优先遍历,接下来使用链式前向星再来模拟一次。要点:由于根节点的未知,要使用额外的bool 数组来标记已历遍过的数据。

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

现在介绍宽度优先遍历,也叫广度优先遍历,指的是将同一层的节点遍历完后再遍历下一层。所以根据队列的特性,我们可以应用queue来完成这个遍历。

我们还是先用vector数组的存储方法来模拟:(不要忘了将已遍历过了的点标记 ,与之前相同)

#include <iostream> #include <vector> #include <queue> using namespace std; const int N = 1e6 + 10; vector<int>edges[N]; int n; bool st[N]; void bfs() { queue<int>q; q.push(1); while (q.size()) { int u = q.front(); q.pop(); cout << u << " "; for (auto v : edges[u]) { if (!st[v]) { q.push(v); st[v] = true; } } } } int main() { cin >> n; for (int i = 1; i < n; i++) { int u, v; edges[u].push_back(v); edges[v].push_back(u); } bfs(); }

再来使用链式前向星来储存时的bfs:

#include <iostream> #include <queue> using namespace std; const int N = 1e6 + 10; int h[N], e[N * 2], ne[N * 2]; int n, id; bool st[N]; void add(int a, int b) { id++; e[id] = b; ne[id] = h[a]; h[a] = id; } void bfs() { queue<int>q; q.push(1); while (q.size()) { int u = q.front(); q.pop(); cout << u << " "; for (int i = h[u]; i; i = ne[i]) { int v = e[i]; if (!st[v]) { q.push(v); st[v] = true; } } } } int main() { cin >> n; for (int i = 1; i < n; i++) { int a, b; cin >> a >> b; add(a, b); add(b, a); } bfs(); }#include <iostream> #include <queue> using namespace std; const int N = 1e6 + 10; int h[N], e[N * 2], ne[N * 2]; int n, id; bool st[N]; void add(int a, int b) { id++; e[id] = b; ne[id] = h[a]; h[a] = id; } void bfs() { queue<int>q; q.push(1); while (q.size()) { int u = q.front(); q.pop(); cout << u << " "; for (int i = h[u]; i; i = ne[i]) { int v = e[i]; if (!st[v]) { q.push(v); st[v] = true; } } } } int main() { cin >> n; for (int i = 1; i < n; i++) { int a, b; cin >> a >> b; add(a, b); add(b, a); } bfs(); }

树的种类还有许多可分为N叉树,我认为树的进阶和之后的节点的捆绑就是类似图的数据结构吧,当然纯属个人想法,等到学到该内容再与大家讨论。

http://www.cnnetsun.cn/news/43440.html

相关文章:

  • 6、高增长、高科技企业的商业模式剖析
  • 基于自抗扰控制ADRC的永磁同步电机仿真模型(Simulink仿真实现)
  • 12、Oracle软件安装、配置、故障排除与卸载全解析
  • 技术文档还在全靠 Markdown?它可能真的在拖你后腿
  • 阿里重磅发布HunyuanCustom视频生成模型 多模态技术引领虚拟内容创作新革命
  • OpenAI开源力作:GPT-OSS模型深度解析与应用指南
  • 基于微信小程序的商品展示计算机毕设(源码+lw+部署文档+讲解等)
  • 【Spring】实现验证码功能
  • 人工智能行业发展新趋势:技术突破与应用拓展并行
  • 8、X Window System使用指南
  • Log4j2 + AI 异常分析:当生产环境报错时,让 AI 自动告诉你 Bug 在哪一行(LogAppender 实战)
  • 11、如何使用 PPP 协议连接互联网
  • 12、OpenLinux 系统互联网邮件配置全攻略
  • 14、互联网下载与浏览指南
  • 9、法医调查中的任务管理与证据组织策略
  • 22、基础系统管理指南
  • 16、数字取证图像的完整性保护与处理
  • 19、数字取证中的磁盘管理与图像管理技巧
  • 25、利用调度实现系统管理自动化
  • 6大AI论文工具实测对比,2025年推荐这几款
  • 6款AI论文工具横向测评,2025年优选榜单出炉
  • 蚂蚁百灵开源混合线性推理模型:Ring-linear系列攻克长文本推理成本难题,吞吐量提升12倍
  • 百度网盘智能提取码解析工具:告别繁琐搜索的全新体验
  • 智能养老新突破:Onscreen平板应用落地 CES 2025,弥合银发群体数字鸿沟
  • Java毕设项目:基于java的教务管理系统学生成绩管理、网上选课、网上报名、教学评价和系统管理(源码+文档,讲解、调试运行,定制等)
  • Java毕设项目:基于Java社交网络平台 基于Java的交友系统(源码+文档,讲解、调试运行,定制等)
  • 28、嵌入式系统中的看门狗与电源管理
  • 38、事件跟踪工具全解析
  • 【URP】Unity[后处理]通道混合ChannelMixer
  • 90%前端都踩过的JS内存黑洞:从《你不知道的JavaScript》解锁底层逻辑与避坑指南