初阶数据结构——二叉树大汇总

这篇博客将会讲到二叉树的部分内容及堆的相关知识~ 这里将会涉及到大量的递归(头大)

目录

1.树

1.1树的概念

1.2树的相关概念

1.3树的表示

1.4树的实际应用

2.二叉树

2.1二叉树的概念

2.2特殊的二叉树 

2.2.1 满二叉树

2.2.2 完全二叉树

2.2.3堆 

2.3 二叉树的性质

2.4二叉树的存储 

2.4.1顺序结构存储

2.4.2链式结构存储

2.5二叉树的遍历

2.5.1前序遍历

代码表示 

 2.5.2中序遍历

代码表示 

2.5.3后续遍历

代码表示 

 2.5.4层序遍历 

代码表示

总结示例 

2.6二叉树的其他代码 


1.树

1.1树的概念

树这个东东,真的不同于之前学到的数据结构 

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因 为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。

  1. 有一个特殊的结点,称为根结点,根结点没有前驱结点
  2. 除根结点外,其余结点被分成M(M>0)个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1<=i<=m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继
  3. 树是递归定义的。 

如图: 

 

注意:子树与子树之间不能相连,不然就变成另一种数据结构——图 

 

1.2树的相关概念

 

结点的度:一个结点含有的子树的个数称为该结点的度; 如上图:A的为6

叶结点或终端结点度为0的结点称为叶结点; 如上图:B、C、H、I...等结点为叶结点

非终端结点或分支结点:度不为0的结点; 如上图:D、E、F、G...等结点为分支结点

双亲结点或父结点若一个结点含有子结点,则这个结点称为其子结点的父结点; 如上图:A是B的父结点

孩子结点或子结点一个结点含有的子树的根结点称为该结点的子结点; 如上图:B是A的孩子结点

兄弟结点:具有相同父结点的结点互称为兄弟结点; 如上图:B、C是兄弟结点

树的度一棵树中,最大的结点的度称为树的度; 如上图:树的度为6

结点的层次从根开始定义起,根为第1层,根的子结点为第2层,以此类推;

树的高度或深度树中结点的最大层次; 如上图:树的高度为4

堂兄弟结点:双亲在同一层的结点互为堂兄弟;如上图:H、I互为兄弟结点

结点的祖先:从根到该结点所经分支上的所有结点;如上图:A是所有结点的祖先

子孙:以某结点为根的子树中任一结点都称为该结点的子孙。如上图:所有结点都是A的子孙

森林:由m(m>0)棵互不相交的树的集合称为森林; 

1.3树的表示

树结构相对线性表结构比较复杂,要存储表示起来比较麻烦,因为既要保存,也要保存结点和结点之间的关系

实际中树有很多种表示方式如:双亲表示法,孩子表示法、孩子双亲表示法以及孩子兄弟表示法 等。我们这里就简单的了解其中最常用的孩子兄弟表示法

所谓孩子兄弟表示法就是左孩子右兄弟(节点的分支左边第一个是孩子,剩下的都是兄弟)如图所示:

1.4树的实际应用

2.二叉树

2.1二叉树的概念

通俗来讲,二叉树就是度为2的树,也就是节点的度最大为2。如下图:

二叉树可以是空树,任何一棵二叉树都由下面几种情况组成:

2.2特殊的二叉树 

2.2.1 满二叉树

一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是 说,如果一个二叉树的层数为K,且结点总数是 ,则它就是满二叉树。也可以叫做正则二叉树

2.2.2 完全二叉树

完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K 的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。

简单来说,就是从第一层到倒数第二层是满二叉树,最后一层从左向右填充(可以不满)

2.2.3堆 

如果有一个关键码的集合K = { k0,k1 ,k2 ,…,kn-1 },把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中,并满足:Ki<=K2i+1 且 Ki<=K2i+2 (Ki>=K2i+1 且 Ki>=K2i+2) i = 0,1, 2…,则称为小堆(或大堆)。将根结点最大的堆叫做最大堆或大根堆,根结点最小的堆叫做最小堆或小根堆。  

简单来说就是:

每一个父亲都比孩子小,叫小堆(头小脚大);每一个父亲都比孩子大,叫大堆(头大脚小)

2.3 二叉树的性质

1. 若规定根结点的层数为1,则一棵非空二叉树的第i层上最多有2^(i-1)个结点。

2. 若规定根结点的层数为1,则深度为h的二叉树的最大结点数是2^h -1。

3. 对任何一棵二叉树, 如果度为0的叶结点个数为n,度为2的分支结个数为m ,则有 m=n - 1。 

4. 若规定根结点的层数为1,具有n个结点的满二叉树的深度,h= log(n+1)  (ps: 是log以2 为底,n+1为对数)

5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有结点从0开始编号,则对于序号为i的结点有:

  1. 若i>0,i位置结点的双亲序号:(i-1)/2;i=0,i为根结点编号,无双亲结点
  2. 若2i+1<n,左孩子序号:2i+1,2i+1>=n则无左孩子
  3. 若2i+2<n,右孩子序号:2i+2,2i+2>=n则无右孩子

2.4二叉树的存储 

二叉树的存储方式有两种,一种顺序结构存储,一种链式结构存储。 

2.4.1顺序结构存储

顺序结构存储也就是用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。 

二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。

2.4.2链式结构存储

二叉树的链式存储结构是指用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。 

 

typedef int BTDataType;
// 二叉链
struct BinaryTreeNode
{struct BinTreeNode* left;// 指向当前结点左孩子struct BinTreeNode* right;// 指向当前结点右孩子BTDataType data;// 当前结点值域
}// 三叉链
struct BinaryTreeNode
{struct BinTreeNode* left;// 指向当前结点左孩子struct BinTreeNode* right;// 指向当前结点右孩子BTDataType data;// 当前结点值域struct BinTreeNode* parent;//指向当前结点的双亲}

2.5二叉树的遍历

这里的遍历就需要用到大量的递归了 

递归是个难点,学习递归就要弄懂两件事:当前问题,子问题

所谓递归,不过是一堆相同步骤的叠加,用很短的代码来处理相同的步骤。

写代码时需要把握好递归的截止条件、每一步递归进行的相同的步骤

二叉树的前、中、后序遍历常用的就是递归方式,当然也可以写非递归方式(这里常用栈的结构、循环来解决) 

2.5.1前序遍历

前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。

前序遍历的过程中,所有节点的访问顺序都是根、左子树、右子树

下面这幅图表示的很形象

代码表示 
// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}//结束条件printf("%c ", root->_data);BinaryTreePrevOrder(root->_left);BinaryTreePrevOrder(root->_right);//相同的每一步}

 2.5.2中序遍历

中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。

中序遍历的过程中,所有节点的访问顺序都是左子树、根、右子树 

代码表示 
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}BinaryTreeInOrder(root->_left);printf("%c ", root->_data);BinaryTreeInOrder(root->_right);
}

2.5.3后续遍历

后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。  

后序遍历的过程中,所有节点的访问顺序都是左子树、右子树、根 

代码表示 
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}BinaryTreePostOrder(root->_left);BinaryTreePostOrder(root->_right);printf("%c ", root->_data);}

 2.5.4层序遍历 

层序遍历是最符合人类习惯的遍历方式 

设二叉树的根结点所在层数为1,层序遍历就是从所在二叉树的根结点出发,首先访问第一层的树根结点,然后从左到右访问第2层上的结点,接着是第3层的结点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。  

  

代码表示

层序遍历的基础要用到数据结构队列, 先让第一个节点入队,后续按照:一个节点出队,该节点的子节点入队,这一方式进行循环遍历

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{Queue q;QueueInit(&q);QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);printf("%c ", front->_data);QueuePop(&q);if(front->_left!=NULL)QueuePush(&q, front->_left);if (front->_right != NULL)QueuePush(&q, front->_right);}printf("\n");}

总结示例 

  • 前序遍历结果:1 2 3 4 5 6
  • 中序遍历结果:3 2 1 5 4 6
  • 后序遍历结果:3 2 5 6 4 1  
  • 层序遍历结果:1 2 4 3 5 6 

2.6二叉树的其他代码 

// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL){return NULL;}if (root->_data == x){return root;}BTNode* ret1 = BinaryTreeFind(root->_left,x);if (ret1){return ret1;}BTNode* ret2 = BinaryTreeFind(root->_left, x);if (ret2){return ret2;}return NULL;
}
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{if (root == NULL){return 0;}if (k == 1){return 1;}int left = BinaryTreeLevelKSize(root->_left, k - 1);int right = BinaryTreeLevelKSize(root->_right, k - 1);return left + right;
}
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{if (root == NULL){return 0;}if (root->_left == NULL && root->_right == NULL){return 1;}return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right) ;}
// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{return root == NULL ? 0 : BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right) + 1;
}

---------------------------------------------------------------------------------------------------------------------------------

好啦二叉树的相关内容就先到这啦,后续应该会有所补充,敬请期待~

看到这里的小伙伴,求求点赞、关注、收藏~

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

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

相关文章

如何用python在大麦网抢票?新手也能学会!

使用Python如何在大麦网抢票 背景介绍 大麦网是一个知名的演出票务平台&#xff0c;很多演唱会、体育赛事等热门活动的门票都可以在这里购买。由于热门场次的门票往往会在开售时秒光&#xff0c;因此抢票成为了很多人的一项技术活。本文将介绍如何使用Python编写程序来自动在大…

基于STM32的逻辑分析仪

文章目录 一、逻辑分析仪体验1、使用示例1.1 逻辑分析仪1.2 开源软件PulseView 2、核心技术2.1 技术方案2.2 信号采集与存储2.3 数据上传 3、使用逻辑分析仪4、 SourceInsight 使用技巧4.1新建工程4.2 设置工程名及工程数据目录4.3 指定源码目录4.4 添加源码4.5 同步文件4.6 操…

在windows上使用Docker部署一个简易的web程序

使用Docker部署一个python的web服务&#x1f680; 由于是从事算法相关工作&#xff0c;之前在项目中&#xff0c;需要将写完的代码服务&#xff0c;部署在docker上&#xff0c;以此是开始接触了Docker这个工具&#xff0c;由于之前也没系统学习过&#xff0c;之后应该可能还会用…

视频压缩大小怎么压缩?几种简单视频压缩方法教给你

现如今&#xff0c;视频已成为我们生活和工作中不可或缺的一部分。然而&#xff0c;高清视频往往伴随着庞大的文件体积&#xff0c;这给存储和传输带来了不小的挑战。这时候我们就需要对视频进行压缩处理&#xff0c;方便储存和发送&#xff0c;那么怎么有效压缩视频呢&#xf…

java之回合制游戏以及如何优化

public class Role {private String name;private int blood;//空参public Role() {}//包含全部参数的构造public Role(String name, int blood) {this.name name;this.blood blood;}public String getName() {return name;}public void setName(String name) {this.name na…

提交高通量测序原始数据到 SRA --- 操作流程

❝ 写在前面 由于最近在提交课题数据到 NCBI 数据库&#xff0c;整理了相关笔记。本着自己学习、分享他人的态度&#xff0c;分享学习笔记&#xff0c;希望能对大家有所帮助。推荐先按顺序阅读往期内容&#xff1a; 1. 提交高通量测序数据到 GEO --- 说明书 目录 1 注册 NCBI 账…

【C++】关联容器探秘:Map与Multimap详解

目录 1.映射类 map 0. 引入 pair&#xff1a; 1.定义 2.插入 3. 遍历 4.❗operator[]的实现 5. 插入 运用 2.Multimap 类 0. 引入&#xff1a;不去重的 Multi 1. Multimap 不支持 Operator[] 2. Multimap 的删除 1.映射类 map 0. 引入 pair&#xff1a; 在C中&…

1 go语言环境的搭建

本专栏将从基础开始&#xff0c;循序渐进&#xff0c;由浅入深讲解Go语言&#xff0c;希望大家都能够从中有所收获&#xff0c;也请大家多多支持。 查看相关资料与知识库 专栏地址:Go专栏 如果文章知识点有错误的地方&#xff0c;请指正&#xff01;大家一起学习&#xff0c;…

软件测试---测试需求分析

课程目标 什么是软件测试需求 软件测试需求的必要性 如何对软件测试需求进行分析&#xff08;重点&#xff09; 课程补充 灰度测试&#xff08;基于功能&#xff09;&#xff1a;先发布部分功能&#xff0c;然后看用户的反馈&#xff0c;再去发布另外一部分的功能更新。 A/B测…

运筹学笔记

计算的时间问题&#xff01;计算机解决了计算量的问题&#xff01; 计算机的发展对运筹学研究起到了极大的促进作用。 运筹学的一个特征之一是它常常会考虑寻求问题模型的最佳解决方案&#xff08;称为最优解&#xff09;。 没有人能成为运筹学所有方面的专家。 分析学越来越流…

C++学习笔记04-补充知识点(问题-解答自查版)

前言 以下问题以Q&A形式记录&#xff0c;基本上都是笔者在初学一轮后&#xff0c;掌握不牢或者频繁忘记的点 Q&A的形式有助于学习过程中时刻关注自己的输入与输出关系&#xff0c;也适合做查漏补缺和复盘。 本文对读者可以用作自查&#xff0c;答案在后面&#xff0…

国内微短剧系统平台抖音微信付费小程序app开发源代码交付

微短剧作为当下热门的内容&#xff0c;结合抖音平台的广泛用户基础&#xff0c;开发微短剧付费小程序APP具有显著的市场潜力&#xff0c;用户对于短剧内容的需求旺盛&#xff0c;特别是在言情、总裁、赘婿等热门题材方面&#xff0c;接下来给大家普及一下微短剧小程序系统。 顺…

rce漏洞-ctfshow(50-70)

Web51 if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\|\%|\x09|\x26/i", $c)){ system($c." >/dev/null 2>&1"); } Nl&#xff0c;绕过tac&#xff0c;cat&#xff0c;绕…

Pytest进阶之fixture的使用(超详细)

目录 Fixture定义 Fixture使用方式 作为参数使用 Fixture间相互调用(作为参数调用) 作为conftest.py文件传入 Fixture作用范围Scope function class module session Fixture中params和ids Fixture中autouse Fixture中Name 总结 pytest fixture 是一种用来管理测试…

回溯算法(相关解题):

求子集序列&#xff1a; 解题思路&#xff1a; 已知原集合的数据位数为N&#xff0c;则可以通过二进制比对原来集合&#xff0c;二进制位为1则输出集合上的该位数据&#xff0c;为0则空&#xff0c;二进制的01排序规律与子集的输出一致由集合的位数可以判断出二进制的范围 0 ~…

P3-AI产品经理-九五小庞

AI产品的数据流向 美团外卖&#xff0c;实时只能调度 美团28分钟送达需求的分析 AI产品常用的算法 常用算法 常见的AI算法解析 自然语言生成NLG语音识别&#xff1a;科大讯飞&#xff0c;通义千问 虚拟现实机器学习平台 决策管理系统生物特征识别技术 RPA(机器人流程自动…

UE4-光照重建

当我们拉入新的光源和模型到我们的场景中后&#xff0c;会产生这样的情况&#xff1a; Preview:预览 表示此时由于光照物体所产生的阴影都是预览级别的并不是真正的效果。 方法一&#xff1a; 或者也可以在世界大纲中选中我们的光源&#xff0c;然后将我们的光源改变为可以…

判断字符串,数组方法

判断字符串方法 在JavaScript中&#xff0c;可以使用typeof操作符来判断一个变量是否为字符串。 function isString(value) {return typeof value string; } 判断数组 在JavaScript中&#xff0c;typeof操作符并不足以准确判断一个变量是否为数组&#xff0c;因为typeof会…

《昇思25天学习打卡营第25天|第27天》

今天是学习的第二十七天&#xff0c;今天学习的是应用实践篇中计算机视觉中ShuffleNet图像分类。 从对ShuffleNet网络介绍开始学习&#xff0c;模型架构&#xff08;Pointwise Group Convolution&#xff0c;Channel Shuffle&#xff0c;ShuffleNet模块&#xff0c;构建Shuffl…

# Redis 入门到精通(九)-- 主从复制(1)

Redis 入门到精通&#xff08;九&#xff09;-- 主从复制&#xff08;1&#xff09; 一、redis 主从复制 – 主从复制简介 1、互联网“三高”架构 高并发高性能高可用 2、你的“Redis”是否高可用&#xff1f; 1&#xff09;单机 redis 的风险与问题 问题1.机器故障  现…