【数据结构】16 二叉树的定义,性质,存储结构(以及先序、后序、中序遍历)

二叉树

一个二叉树是一个有穷的结点集合。
它是由根节点和称为其左子树和右子树的两个不相交的二叉树组成的。
二叉树可具有以下5种形态。
在这里插入图片描述

性质

  1. 一个二叉树第i层的最大结点数为 2 i − 1 2^{i-1} 2i1, i ≥ 1 i \geq 1 i1
    每层最大结点可以对应完美二叉树(满二叉树),其所有分支结点都存在左右子树,并且所有叶结点都在同一层上。
    在这里插入图片描述
  2. 深度为k的二叉树有最大结点总数: 2 k − 1 2^k-1 2k1, k ≥ 1 k \geq 1 k1
    1 + 2 + . . . + 2 k − 1 = 2 k − 1 1 + 2 + ... +2^{k-1} = 2^k-1 1+2+...+2k1=2k1
  3. 对任何非空的二叉树 T n T_n Tn,若 n 0 n_0 n0是叶结点的个数, n 2 n_2 n2是度为2的非叶结点的个数,则: n 0 = n 2 + 1 n_0 = n_2 +1 n0=n2+1
    一颗二叉树:总结点数 = 叶节点 + 度为1的结点 + 度为2的结点
    又:总结点数 = 总边数+1
    且:总边数 = 2 ∗ 2* 2度为2的结点 + 度为1的结点
    由此得到: n 0 = n 2 + 1 n_0 = n_2 +1 n0=n2+1
  4. 具有n个结点的完全二叉树的深度k为 ⌊ l o g 2 n ⌋ + 1 \lfloor log_2{n} \rfloor +1 log2n+1
    (1) 满二叉树时:
    k深度: 结点 2 k − 1 2^k-1 2k1
    n = 2 k − 1 n = 2^k-1 n=2k1 k = l o g 2 n + 1 k = log_2{n+1} k=log2n+1
    (2)最底层只有一个结点
    k深度: 结点 2 k − 1 2^{k-1} 2k1
    解得: k = l o g 2 n + 1 k = log_2{n} +1 k=log2n+1
    l o g 2 ( n + 1 ) ≤ k ≤ l o g 2 n + 1 log_2{(n+1)} \leq k \leq log_2n +1 log2(n+1)klog2n+1
    则具有n个结点的完全二叉树的深度k为 ⌊ l o g 2 n ⌋ + 1 \lfloor log_2{n} \rfloor +1 log2n+1

存储结构

顺序存储

这种结构是川一组连续的存储单元(比如数组)存储二叉树结点的数据,结点的父子系是通过它们相对位置来反映的,而不需要任何附加的存储单元来存放指针,通常情况下顺序存储结构用于完全二叉树
具体实现是从树的根结点开始,从上层至下层,每层从左到右,依次给结点编号并将数据存放到一个数组的对应单元中。
结点C的父结点是结点B,它的左孩子是结点w,右孩子是结点K。C结点存储单元的下标是4,将其除以2得到它的父结点B的存储单元下标,而将其乘以2则是它的左孩子w存储单元的下标,当然将其乘2再加1则是它右孩子K的存储单元下标。
在这里插入图片描述
在这里插入图片描述

链式存储

虽然顺序存储的空间利用率高,计算简单,但是其不适于一般的二叉树
如图为给定的二叉树。给出了从上至下、从左至右的层序存储的对应结点编号,其中灰色结点是为了满足顺序存储要求而增加的“虚”结点,可以在相应的存储单元存放一个特殊的数值,以区别于其他“实结点”。图4.10(c)则是最终的存储结果。
可以看到,5个结点的二叉树,顺序存储需要13个存储单元,超过一半的存储空间浪费掉了。更有甚者,对一个深度为h的右斜二叉树来讲,需要2-1个存储单元,而实际上该斜二叉树只有k个结点。

另外,二叉树的顺序存储方式避免不了顺序存储的固有缺点,即不易实现增加、删除操作。因此,二叉树的顺序存储方式适用于一定的条件,对于不需要修改的完全二叉树,是一种较好的选择。
实际上,二叉树的最常用表示方法是用链表表示,每个结点由数据和左右指针三个数据成员组成。

结构定义

typedef int ElementType;
typedef struct TNode* Position;
typedef Position BinTree;
struct TNode {ElementType Data;//结点数据BinTree Left; //指向左子树BinTree Right; //指向右子树
};

操作实现

遍历

我们用L,V,R分别表示遍历左分支L,访问结点V,遍历右分支R,那么可以有以下6种情况:LVR,LRV,VLR,VRL,RLV,RVL。
规定:访问左分支在右分支之前,只剩下:LVR, LRV, VLR。
我们按照V的位置分别将其命名为:中序遍历,后序遍历,先序遍历

中序遍历

对树的任一结点的访问是在先遍历完其左子树后进行的,访问此结点后,在对其右子树遍历
遇到每个结点,其遍历过程

  1. 中序遍历左子树
  2. 访问根节点
  3. 中序遍历右节点
void InorderTraveral(BinTree BT) {if (BT) {InorderTraveral(BT->Left);printf("%d\n, BT->Data");InorderTraveral(BT->Right);}
}
后序遍历

对结点的左右子树先进行遍历,然后才对此结点访问。遍历是从根节点开始,遇到每个结点时,其遍历过程是:

  1. 后序遍历其左子树
  2. 后序遍历其右子树
  3. 访问根节点
void PostorderTraversal(BinTree BT) {if (BT) {PostorderTraversal(BT->Left);printf("%d\n, BT->Data");PostorderTraversal(BT->Right);}
}
先序遍历

对结点的访问是在其左、右子树遍历之前进行的。遍历是从根节点开始,遇到每个结点时,其遍历过程是:

  1. 访问根结点
  2. 先序遍历其左子树
  3. 先序遍历其右子树
void PreorderTraversal(BinTree BT) {if (BT) {printf("%d\n, BT->Data");PreorderTraversal(BT->Left);PreorderTraversal(BT->Right);}
}
非递归遍历

在沿左子树深入时,进入一个结点就将其压入堆栈。
若是先序遍历,则在入栈之前访问之;当沿左分支深入不下去时,则返回,即从堆栈中弹出前面压入的结点;
若为中序遍历,则此时访同该结点,然后从该结点的右子树继续深入;
若为后序遍历,则将此结点二次入栈,然后从该结点的右子树继续深入,与前面类同,仍为进入一个结点入栈一个结点,深入不下去再返回,直到第二次从栈里弹出该结点,才访问之。

对于非递归中序遍历,遇到一个节点就将其压栈,并去遍历其左子树;当左子树结束后,从栈顶弹出结点并访问它,然后按其右指针再去中序遍历该节点的右子树。

void InorderTraversalUn(BinTree BT) {BinTree T;Stack S = CreateStack(100);T = BT;while (T || !IsEmpty(S)) {while (T) {Push(S, T);T = T->Left;}T = Pop(S);printf("%d\n, T->Data");T = T->Right;}}
层序遍历

层序遍历是按照树的层次,从第一层的根结点开始向下逐层访问每个结点,对每一层的结点按照从左到右的顺序访问。
可以设置一个队列结构,遍历从根节点开始,首先将根节指针入队,然后执行以下操作:

  1. 从队列取出一个元素
  2. 访问该元素所指向的结点
  3. 若元素所指向的结点的左右孩子非空,将其左、右孩子的指针入队。
    不断执行这三步,直到队列为空。
void LevelorderTraversal(BinTree BT) {Queue Q;BinTree T;T = BT;Q = CreateQueue(100);AddQ(Q, T);while (!IsEmptyQ(Q)) {T = DeleteQ(Q);printf("%d\n, T->Data");if (!T->Left) { AddQ(Q, T->Left); }if (!T->Right) { AddQ(Q, T->Right); }}
}

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

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

相关文章

CSDN如何获得更多勋章?

文章目录 前言一、如何找到自己的勋章?二、如何获得更多勋章?三、重点勋章、易得勋章介绍&推荐1.创作能手2.五一创作勋章3.创作纪念日IT一周年勋章4.新秀勋章5.话题达人6.128天创作纪念日(IT博客专属)7.GitHub绑定勋章8.其他 …

TIM(Timer)定时中断 P1

难点:定时器级联、主从模式 一、简介: 1.TIM(Timer)定时器 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断 补充: { 定时器本质上是一个计数器,可以工作在定时或计数模式&…

如何简单上手清华AutoGPT并搭建到本地环境

一、准备工作 安装Docker:确保你的本地机器上已经安装了Docker。如果还没有安装,请访问Docker官方网站并按照指引进行安装。--点击进入Docker官网 获取清华AutoGPT的Docker镜像:清华AutoGPT团队可能已经提供了一个Docker镜像,方便…

java8新特性——StreamAPI

说明: java8中有两大最为重要的改变。第一个是Lambda表达式;另外一个则是Stream API。 Stream API(java.util.stream)把真正的函数式编程风格引入java。这是目前为止对java类库最好的补充,因为Stream API可以极大提供j…

AI:130-基于深度学习的室内导航与定位

🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的关键代码,详细讲解供…

红蓝对抗:网络安全领域的模拟实战演练

引言: 随着信息技术的快速发展,网络安全问题日益突出。为了应对这一挑战,企业和组织需要不断提升自身的安全防护能力。红蓝对抗作为一种模拟实战演练方法,在网络安全领域得到了广泛应用。本文将介绍红蓝对抗的概念、目的、过程和…

rtt设备io框架面向对象学习-硬件rtc设备

目录 1.硬件rtc设备基类2.硬件rtc设备基类的子类3.初始化/构造流程3.1设备驱动层3.2 设备驱动框架层3.3 设备io管理层 4.总结5.使用 1.硬件rtc设备基类 此层处于设备驱动框架层。此层的类是抽象类。 在/ components / drivers / include / drivers 下的rtc.h定义了如下rtc设备…

C# 教程 目录

17.3 图像处理17.3.1 像素处理17.3.1.1 逆反(底片)17.3.1.2 曝光17.3.1.3 灰度17.3.1.4 浮雕17.3.1.5 二值化(黑白)17.3.1.6 自定义处理17.3.2 内存处理17.3.2.1 Bitmap.LockBits方法和Bitmap.UnlockBits方法17.3.2.2 BitmapData类…

Windows11即将停止支持部分旧CPU

Windows11的官方硬件需求高于Windows10,当然网上有很多插件,可以绕过硬件检查,升级win10到win11。 在一些低配的Windows10 PC上运行Windows11基本不会带来任何后果。通过破解插件,一般用户甚至可以在WindowsXP时代的英特尔酷容2&…

【笔记】常用方法

Arrays工具类 使用Arrays.sort( )对自定义类进行排序的时候&#xff0c;这个类需要实现(implements) Comparable<类名>&#xff0c;然后重写compareTo( )这个方法。 Lambda表达式 Lambda表达式&#xff0c;必须要有一个接口类&#xff0c;并且这个接口类中只有有一个抽象…

硬盘合并分区失败显示未格式化怎么办?看这里

在当今数字化时代&#xff0c;硬盘分区已成为计算机存储管理的重要手段。然而&#xff0c;在使用过程中&#xff0c;我们有时会遇到“合并分区失败显示未格式化”的问题&#xff0c;这不仅影响了我们的工作效率&#xff0c;还可能造成数据丢失的风险。下面将根据不同情况给予不…

C语言什么叫逻辑短路?

一、问题 只有电路中才听说过短路。逻辑短路&#xff0c;不像是什么好词⼉。这个词不是描述⼈的&#xff0c;是描述C语⾔程序的。它是程序执⾏时的⼀种特殊的跳跃状态。那么逻辑短路具体是什么呢&#xff1f; 二、解答 C语⾔中&#xff0c;表⽰条件时⽤关系表达式&#xff0c;…

c# BlockingCollection 清空

BlockingCollection<T>在C#中是一个线程安全的集合&#xff0c;用于并发场景。它不提供一个直接的Clear方法&#xff0c;因为这样在多线程环境下可能会引入竞争条件和其他线程安全问题。 如果你真的需要清空这个集合&#xff0c;你可以通过手动取出每一个元素来实现。以下…

数据库事务的4个特性(ACID)

数据库事务具有四个重要的特性&#xff0c;通常称为ACID特性&#xff0c;分别是原子性&#xff08;Atomicity&#xff09;、一致性&#xff08;Consistency&#xff09;、隔离性&#xff08;Isolation&#xff09;和持久性&#xff08;Durability&#xff09;。下面分别对四种特…

【LeetCode: 107. 二叉树的层序遍历 II + BFS】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

相机的机身马达有什么用?

新手疑问&#xff1a; 为什么我的尼康D3200相机明明拥有拍视频能力&#xff0c;但是拍摄视频时却不能对焦 科普时间 那是因为你的相机缺少机身马达&#xff0c;并且你所使用的镜头也没有马达!机身马达是用于给镜头提供对焦动力的装置。它的作用是使相机具备自动对焦功能。如…

TCP常见问题详解

本篇文章我们介绍一下 在我们的面试中和实际开发中使用TCP遇到的问题 1.TCP在什么情况下出现大量的time_event 什么是time_event&#xff1f; 我们首先要弄清楚TIME_WAIT状态是什么&#xff1f;TIME_WAIT状态是主动关闭TCP连接的一方&#xff08;即先发起FIN包的一方&#xf…

node+vue3+mysql前后分离开发范式——实现视频文件上传并渲染

文章目录 ⭐前言⭐ 功能设计与实现💖 node上传文件写入file_map映射表💖 vue3前端上传文件回显⭐ 效果⭐结束⭐前言 大家好,我是yma16,本文分享关于 node+vue3+mysql前后分离开发范式——实现视频文件上传并渲染。 技术选型 前端:vite+vue3+antd 后端:node koa 数据库…

有了NULL,为什么C++还需要nullptr?

目录 1.引言 2.类型安全 3.函数重载 4.代码清晰性 5.示例 6.总结 1.引言 在C编程中&#xff0c;nullptr是一个类型安全的空指针常量&#xff0c;自C11起被引入。然而&#xff0c;在此之前&#xff0c;程序员们通常使用NULL或0来表示空指针。那么&#xff0c;为什么有了N…

在 MySQL 数据库中创建新账号并分配权限

本文介绍如何在 MySQL 数据库中创建新账号并为其分配特定数据库实例的权限。 一、连接到 MySQL 数据库 首先&#xff0c;使用 MySQL 客户端或其他数据库管理工具连接到 MySQL 数据库服务器。你可以使用以下命令。 mysql -u root -p 然后输入密码以登录到 MySQL。 二、创建新…