数据结构之二叉树

 树的简介:

 

 再来看看二叉树的简介:

 容易想到p叉树就是每个节点最多有p个子节点的树。

接下来看两种特殊的二叉树:

接下来我们思考两个问题:

1.深度为h的满二叉树一共有多少个节点?

对于这一个问题,我们观察满二叉树的结构,会发现最后的答案等于2^0+2^1+2^2+...+2^(h-1)=2^h-1

2.深度为h的完全二叉树的节点数范围是多少?

根据完全二叉树的定义可以知道它最后一层上面的所有部分都满足满二叉树的特征并且最后一层具有一个分界点,分界点的左边节点全部都是存在的而分界点的右边一个节点都没有 。

所以我们可以知道,深度为h的完全二叉树最小节点数为2^0+2^1+2^2+...+2^(h-2)=2^(h-1) -1+1。

而其最多的节点数和满二叉树是一样的。

接下来看完全二叉树的存储与建立:

接下来是完全二叉树的建立:

通常我们是通过递归的方式对完全二叉树进行建立:

 上述蓝色部分的代码就是用以建立一个以t节点为根的子树。

需要注意的是如果用这种方式想去建一个非完全二叉树的话将会导致数组中部分下标的浪费,因为对于一个确定的节点只能建立下标为它左右儿子节点的子节点,而不能保证与他下标相邻的树的节点的建立。所以我们可以知道,是否可以用这样建立树的方式去解决问题取决于问题本身,如果浪费了空间并不影响问题的解答,那么就可以用这样的方法去解答,反之则不可以。

接下来看一下一般二叉树的存储:

为了解决建立一个非完全二叉树,达到不浪费太大空间的目的,衍生出了一般二叉树的存储方法:

 同样的我们可以考虑用指针来存储二叉树:

所以我们可以总结一下二叉树的基本操作有:

 二叉树的遍历顺序:

首先是先序遍历(根左右):

可以看出这样的遍历方式是通过递归的方式实现,从根节点开始进行递归,根据每个节点是否存在左右节点来依次遍历,同时先遍历左然后遍历右。 

 接下来看中序遍历(也称为左根右):

 给我的感觉其实就像是以最开始的p节点开始一直先递归左节点然后反正输出,同时在反着输出的过程中如果相应左节点存在右节点,也会进行输出,文字不是很好表达,读者可以仔细去理解。

 最后再来看一下后序遍历(左右根):

可以看出来这样的遍历顺序就是先从节点p开始,然后依次枚举其左右节点并使用递归的方式枚举到底,最后当不存在子节点的时候就进行输出。

接下来还有一种遍历叫做层级遍历:

 BFS(也叫做宽度优先搜索),这里蕴含了该种搜索的思想:

严格来说,在层级遍历中,我们只需要按照从上往下的顺序遍历所有点,同一层的点的先后顺序不做要求;简单来说,层级遍历指的是我们按深度从小到大的顺序一层层遍历所有点。

接下来就是运用二叉树的基本知识去解答的几道帮助巩固学习的例题:

1.遍历完全二叉树:

 代码如下:

#include<bits/stdc++.h>
using namespace std;
int n;
inline void preorder(int x){cout<<x<<' ';if(x+x<=n) preorder(x+x);if(x+x+1<=n)  preorder(x+x+1);
}
inline void inorder(int x){if(x+x<=n) inorder(x+x);cout<<x<<' ';if(x+x+1<=n) inorder(x+x+1);
}
inline void postorder(int x){if(x+x<=n)   postorder(x+x);if(x+x+1<=n) postorder(x+x+1);cout<<x<<' ';
}
int main(){cin>>n;preorder(1);cout<<endl;inorder(1);cout<<endl;postorder(1);cout<<endl;return 0;
}

其中的inline的作用解释如下:

 2.遍历一般二叉树:

给出两种方法解答,以下是第一种方法的代码:

#include<bits/stdc++.h>
using namespace std;
int n;
struct treenode{int l,r,fa;
}a[1010];
inline void preorder(int x){cout<<x<<' ';if(a[x].l) preorder(a[x].l);if(a[x].r) preorder(a[x].r);
}
inline void inorder(int x){if(a[x].l) inorder(a[x].l);cout<<x<<' ';if(a[x].r) inorder(a[x].r);
}
inline void postorder(int x){if(a[x].l) postorder(a[x].l);if(a[x].r) postorder(a[x].r);cout<<x<<' ';
}
int main(){cin>>n;for(int i=1;i<=n;i++){int x,y;cin>>x>>y;if(x) a[i].l=x;if(y) a[i].r=y;a[x].fa=a[y].fa=i;}preorder(1);cout<<endl;inorder(1);cout<<endl;postorder(1);cout<<endl;return 0;
}

第二种使用指针的方法来解答本题:

#include<bits/stdc++.h>
using namespace std;
int n;
struct treenode{int value;treenode *l,*r,*fa;
}a[1100];
inline void preorder(treenode *t){if(t->value) cout<<t->value<<' ';if(t->l) preorder(t->l);if(t->r) preorder(t->r);
}
inline void inorder(treenode *t){if(t->l) inorder(t->l);cout<<t->value<<' ';if(t->r) inorder(t->r); 
}
inline void postorder(treenode *t){if(t->l) postorder(t->l);if(t->r) postorder(t->r);cout<<t->value<<' ';
}
int main(){cin>>n;for(int i=1;i<=n;i++){int x,y;cin>>x>>y;a[i].value=i;if(x) {a[i].l=&a[x];a[x].fa=&a[i];}if(y){a[i].r=&a[y];a[y].fa=&a[i];	}}preorder(&a[1]);	cout<<endl;inorder(&a[1]);cout<<endl;postorder(&a[1]);cout<<endl;return 0;
}

其实本题完全不用使用第二种方法进行解答,只是单纯的练习一下指针的使用来解答这道题。

3.二叉树的最近公共祖先:

 

 这个题的思路就是利用两个数组来分别存储u与v的祖先,最后存储的祖先一定就是根,所以最开始出现的一样的祖先就是要输出的,代码如下:

#include<bits/stdc++.h>
using namespace std;
int n;
struct treenode{int l,r,fa;
}a[1001];
int c[1001],d[1001];
int main(){cin>>n;for(int i=1;i<=n;i++){int x,y;cin>>x>>y;if(x){a[i].l=x;a[x].fa=i;}if(y){a[i].r=y;a[y].fa=i;}}int l1,l2;l1=l2=0;int u,v;cin>>u>>v;while(u!=1){c[++l1]=u;u=a[u].fa;}c[++l1]=1;while(v!=1){d[++l2]=v;v=a[v].fa;}d[++l2]=1;int x;for(int i=l1,j=l2;j&&i;j--,i--){if(c[i]==d[j]) x=c[i];else break; }cout<<x<<endl;return 0;
}

这种做法相对来说比较容易并且也比较容易理解。最后的x就是答案,他就是c,d数组从根往下看,最后一个相同的值,这也就是u和v的最近公共祖先。

接下来再提供一种更为复杂的做法,也就是按层级遍历的方法来写:

#include<bits/stdc++.h>
using namespace std;
int n,q[1025];
struct treenode{int depth;int l,r,fa;
}a[1025];
int main(){cin>>n;for(int i=1;i<=n;i++){int x,y;cin>>x>>y;if(x){a[i].l=x;a[x].fa=i;}if(y){a[i].r=y;a[y].fa=i;}}int front,rear;front=rear=1;q[1]=1;a[1].depth=1;while(front<=rear){int p=q[front];++front;if(a[p].l){q[++rear]=a[p].l;a[a[p].l].depth=a[p].depth+1;}if(a[p].r){q[++rear]=a[p].r;a[a[p].r].depth=a[p].depth+1;}}int u,v;cin>>u>>v;if(a[u].depth<a[v].depth)swap(u,v);int x=a[u].depth-a[v].depth;for(int i=1;i<=x;i++)u=a[u].fa;while(u!=v){u=a[u].fa;v=a[v].fa;}cout<<u<<endl;return 0;
}

4.二叉树节点子树和:

 注意到有两个数据范围,如果是较小的,那么:

#include<bits/stdc++.h>
using namespace std;
int n;
struct treenode {int value;int l, r, fa;
} a[1000025];
int calculate(int x) {int ans = a[x].value;if (a[x].l) {ans += calculate(a[x].l); // 递归计算左子树的和}if (a[x].r) {ans += calculate(a[x].r); // 递归计算右子树的和}return ans;
}
int main() {cin >> n;for (int i = 1; i <= n; i++) {int x, y;cin >> x >> y;if (x) {a[x].fa = i;a[i].l = x;}if (y) {a[y].fa = i;a[i].r = y;}}for (int i = 1; i <= n; i++) {cin >> a[i].value;}for (int i = 1; i <= n; i++) {cout << calculate(i) << ' ';}return 0;
}

以下这种写法或许更好理解函数部分:

#include<bits/stdc++.h>
using namespace std;
int n;
struct treenode{int value;int l,r,fa;
}a[1025];
int ans;
inline void calculate(int x){ans+=a[x].value;if(a[x].l) calculate(a[x].l);if(a[x].r) calculate(a[x].r);
}
int main(){cin>>n;for(int i=1;i<=n;i++){int x,y;cin>>x>>y;if(x){a[x].fa=i;a[i].l=x;	}if(y){a[y].fa=i;a[i].r=y;}}for(int i=1;i<=n;i++){cin>>a[i].value;}for(int i=1;i<=n;i++){ans=0;calculate(i);cout<<ans<<' ';}return 0;
}

如果数据范围是较大的那么上述代码必定就会超时了,这时候就不能使用dfs了,考虑这么写:

#include<bits/stdc++.h>
using namespace std;
int n,cnt,v[1000001];
struct treenode{int value;int l,r,fa;
}a[1000001];
int solve(int t){int x=a[t].value;if(a[t].l) x+=solve(a[t].l);if(a[t].r) x+=solve(a[t].r);v[t]=x;return x;
}
int main(){cin>>n;for(int i=1;i<=n;i++){int x,y;cin>>x>>y;if(x){a[x].fa=i;a[i].l=x;	}if(y){a[y].fa=i;a[i].r=y;}}for(int i=1;i<=n;i++){cin>>a[i].value;}solve(1);for(int i=1;i<=n;i++){cout<<v[i]<<' ';}return 0;
}

这样其实主要是因为考虑到一开始在算第一个子树的左右节点,也就是根的所有子节点的权值和的时候已经把二叉树完全的遍历了一遍,所以在后续不断的递归过程中,很多都重复递归了,所以实际上只需要递归一次,然后利用了一个数组v来存储每个节点的所有子节点加上他本身的权值之和。

谢谢观看!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BDD - Python Behave 用户自定义命令行选项 -D

BDD - Python Behave 用户自定义命令行选项 -D 引言behave -Dbehave -D 应用feature 文件behave.ini 配置文件step 文件执行 引言 日常运行测试用例&#xff0c;有时需要自定义命令行参数&#xff0c;比如不同环境的对应的配置是不一样的&#xff0c;这样就需要传一个环境参数…

2023版本QT学习记录 -11- 多线程的使用(QT的方式)

———————多线程的使用(QT方式)——————— &#x1f384;效果演示 两个线程都输出一些调试信息 &#x1f384;创建多线程的流程 &#x1f384;头文件 #include "qthread.h"&#x1f384;利用多态重写任务函数 class rlthread1 : public QThread {Q_OBJE…

C++程序设计兼谈对象模型(侯捷)笔记

C程序设计兼谈对象模型&#xff08;侯捷) 这是C面向对象程序设计的续集笔记&#xff0c;仅供个人学习使用。如有侵权&#xff0c;请联系删除。 主要内容&#xff1a;涉及到模板中的类模板、函数模板、成员模板以及模板模板参数&#xff0c;后面包含对象模型中虚函数调用&…

【计算机毕业设计】SSM企业OA管理系统

项目介绍 本项目包含管理员与普通员工两种角色&#xff0c; 管理员角色包含以下功能&#xff1a; 岗位管理,部门管理,工龄奖金管理,员工管理,考勤管理,工资查询,职称管理,统计图表,工资项管理,管理员登录等功能。 员工角色包含以下功能&#xff1a; 个人信息管理,工资详情…

基于PHP的花店管理系统

有需要请加文章底部Q哦 可远程调试 基于PHP的花店管理系统 一 介绍 此花店管理系统基于原生PHP开发&#xff0c;数据库mysql&#xff0c;前端bootstrap。系统角色分为用户和管理员。(附带参考设计文档) 技术栈&#xff1a;phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 …

[蓝桥 2023 ]三带一

问题描述 小蓝和小桥玩斗地主&#xff0c;小蓝只剩四张牌了&#xff0c;他想知道是否是“三带一”牌型。 所谓“三带一”牌型&#xff0c;即四张手牌中&#xff0c;有三张牌一样&#xff0c;另外一张不与其他牌相同&#xff0c;换种说法&#xff0c;四张手牌经过重新排列后&am…

使用jmeter从0开始完成性能测试

使用JMeter从0开始完成性能测试 介绍 在软件开发过程中&#xff0c;性能测试是一项关键任务&#xff0c;它可以帮助我们评估系统在不同负载条件下的性能表现&#xff0c;发现潜在的性能瓶颈。JMeter是一款功能强大且易于使用的性能测试工具&#xff0c;它可以帮助我们完成各种…

网络故障排查和流量分析利器-Tcpdump命令

Tcpdump是一个在Unix/Linux系统上广泛使用的命令行网络抓包工具。它能够捕获经过网络接口的数据包&#xff0c;并将其以可读的格式输出到终端或文件中。Tcpdump是一个强大的命令行工具&#xff0c;能够捕获和分析网络数据包&#xff0c;为网络管理员和安全专业人员提供了深入了…

手写视频裁剪框

<!-- 截取框 --><divv-show"isShow"class"crop-box":style"{width: cropWidth px,height: cropHeight px,left: cropX px,top: cropY px,}"ref"cropBox"mousedown"startInteraction"><!-- 内容在这里 --…

mysql 单表 操作 最大条数验证 以及优化

1、背景 开车的多年老司机&#xff0c;是不是经常听到过&#xff0c;“mysql 单表最好不要超过 2000w”,“单表超过 2000w 就要考虑数据迁移了”&#xff0c;“你这个表数据都马上要到 2000w 了&#xff0c;难怪查询速度慢”。 2、实验 实验一把看看… 建一张表 CREATE TABL…

Java BIO、NIO、AIO、Netty知识详解(值得珍藏)

1. 什么是IO Java中I/O是以流为基础进行数据的输入输出的&#xff0c;所有数据被串行化(所谓串行化就是数据要按顺序进行输入输出)写入输出流。简单来说就是java通过io流方式和外部设备进行交互。 在Java类库中&#xff0c;IO部分的内容是很庞大的&#xff0c;因为它涉及的领…

[Ray Tracing: The Rest of Your Life] 笔记

前言 开年第一篇博客~ 整理了三四个小时才整理完orz。 这一部分是光线追踪三部曲的最后一部&#xff0c;主要介绍了蒙特卡洛积分、重要性采样等内容。场景上没有什么大的改变&#xff0c;基本上就是在Cornell Box中渲染的&#xff0c;本篇主要在加速收敛&#xff0c;提升渲染效…

Docker 安装Mysql

目录 Docker Mysql安装 ✨安装和配置mysql ✨远程连接mysql远程连接 MySQL 是世界上最流行的开源数据库。根据 DB-Engines的调查数据&#xff0c;MySQL 是第二受欢迎的数据库&#xff0c;仅次于 Oracle 数据库。MySQL在过去由于性能高、成本低、可靠性好&#xff0c;已经成…

五、HTML 标题

在 HTML 文档中&#xff0c;标题很重要。 一、HTML 标题 标题&#xff08;Heading&#xff09;是通过 <h1> - <h6> 标签进行定义的。<h1> 定义最大的标题。 <h6> 定义最小的标题。 <h1>这是一个标题。</h1> <h2>这是一个标题。&l…

分类预测 | Python实现基于SVM-RFE-LSTM的特征选择算法结合LSTM神经网络的多输入单输出分类预测

分类预测 | Python实现基于SVM-RFE-LSTM的特征选择算法结合LSTM神经网络的多输入单输出分类预测 目录 分类预测 | Python实现基于SVM-RFE-LSTM的特征选择算法结合LSTM神经网络的多输入单输出分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 基于SVM-RFE-LSTM的特征…

bootstrap5实现宠物商店网站 Cat-Master

一、需求分析 宠物商店网站是指专门为宠物商店或宠物用品商家而建立的在线平台。这种网站的功能通常旨在提供以下服务&#xff1a; 产品展示&#xff1a;宠物商店网站通常会展示宠物食品、玩具、床上用品、健康护理产品等各种宠物用品的图片和详细信息。这样&#xff0c;潜在的…

MybatisPlus—快速入门

目录 1.使用MybatisPlus的基本步骤 1.1引入MybatisPlus的起步依赖 1.2 定义Mapper 2.MybatisPlus常用注解 2.1 TableName 2.2 TableId 2.3 TableField 2.4 小结 3. 常用配置 4. 总结 1.使用MybatisPlus的基本步骤 1.1引入MybatisPlus的起步依赖 MyBatisPlus官方提…

安徽省暨合肥市“希望工程·梦想计划”小盖茨机器人捐赠启动仪式举行

1月5日&#xff0c;安徽省暨合肥市“希望工程梦想计划”小盖茨机器人捐赠启动仪式在合肥市一六八玫瑰园学校东校区举行。共青团安徽省委副书记叶征&#xff0c;北京儒布特教育科技有限公司董事牛俊明&#xff0c;北京儒布特教育科技有限公司市场总监高进&#xff0c;安徽省青基…

基于JavaWeb+SSM+Vue四六级词汇微信小程序系统的设计和实现

基于JavaWebSSMVue四六级词汇微信小程序系统的设计和实现 源码获取入口KaiTi 报告Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 KaiTi 报告 &#xff08;1&#xff09;课题背景 伴随着社会的快速发展, 现代社…

【ASP.NET Core 基础知识】--环境设置

一、简介 1.1 .NET Core SDK 概述 .NET Core SDK&#xff08;Software Development Kit&#xff09;是Microsoft推出的一个开源跨平台框架&#xff0c;用于开发和部署.NET应用程序。它是.NET Core平台的核心组件之一&#xff0c;为开发者提供了在多个操作系统上构建高性能、可…