C语言树与二叉树基础全刨析
作者:CodeWinter 发布时间:2023-01-25 16:06:57
一、树的概念和结构
1.1 树的概念
树是一种非线性的数据结构,它是由 n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
有一个特殊的结点,称为根结点,根节点没有前驱结点
除根节点外,其余结点被分成 M (M>0) 个互不相交的集合T1、T2、……、Tm,其中每一个集合 Ti (1<= i <= m) 又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继
因此,树是递归定义的。
注意:树形结构中,子树之间不能有交集,否则就不是树形结构。
1.2 树的结构 & 相关名词解释
树中的一些名词解释:
节点的度:一个节点的子树 (子节点) 个数称为该节点的度; 如上图:A的度为3
叶节点 (终端节点):度为0的节点称为叶节点; 如上图:J、F、K、L、H、I 节点为叶节点
非终端节点 (分支节点):度不为0的节点; 如上图:B、C、D、E、G 节点为分支节点
双亲节点 (父节点):若一个节点有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点
孩子节点 (子节点):一个节点含有的子树的根节点称为该节点的子节点; 如上图:B是A的孩子节点
兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图:B、C、D是兄弟节点
树的度:一棵树中,最大的节点的度称为树的度; 如上图:树的度为3
节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
树的高度 (深度):树中节点的最大层次; 如上图:树的高度为4,空树的高度是0,只有根节点的树高度为1
堂兄弟节点:双亲在同一层的节点互为堂兄弟;如上图:F、G互为堂兄弟节点
节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先,K的祖先是A、C、G
子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙
森林:由 m (m>0) 棵互不相交的树的集合称为森林(并查集就是一个森林)
1.3 树的表示
树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了,既然保存值域,也要保存结点和结点之间的关系。实际中树有很多种表示方式如:双亲表示法,孩子表示法、孩子双亲表示法、孩子兄弟表示法等。我们这里就简单的了解其中最常用的孩子兄弟表示法。
定义树的结构,首先需要说明树的度是多少,否则很难去定义。
struct TreeNode
{
int data; // 数据域
struct TreeNode* subs[3]; // 此树的度为3
};
如果没有说明树的度是多少,还可以用顺序表去存储。
struct TreeNode
{
int data; // 数据域
SeqList subs; // 顺序表中存储的是树节点指针
// C++可以这样写:vector<struct TreeNode*> subs;
};
再介绍一种双亲表示法,树的结构中,往下走,孩子节点可能有很多,但往上走,每个节点的双亲结点只有一个。
struct TreeNode
{
int data; // 数据域
struct TreeNode* parent; // 记录该节点的双亲结点
};
上述方法都不是很实用,最实用的表示方法是孩子兄弟表示法。左孩子右兄弟。
typedef int DataType;
struct Node
{
DataType _data; // 结点中的数据域
struct Node* _firstChild; // 指向第一个孩子结点(即最左边的孩子节点)
struct Node* _pNextBrother; // 指向右边的第一个兄弟结点
};
1.4 树的应用
表示文件系统的目录树结构
二、二叉树的概念 & 存储结构(重要)
2.1 二叉树的概念
一棵二叉树是结点的一个有限集合,该集合:
由一个根节点加上两棵别称为左子树和右子树的二叉树组成,或者为空
观察上图,二叉树的特点:
二叉树不存在度大于2的结点 (每个节点最多有两个孩子)。
二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树。
注意:对于任意的二叉树都是由以下几种情况复合而成的:
2.2 特殊的二叉树
满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为 K,且结点总数是 2k - 1 ( 20 + 21 + 22 + … + 2k-1 ),则它就是满二叉树。
完全二叉树:
完全二叉树是效率很高的数据结构。
一个深度为 K 的有 n 个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,当且仅当其每一个结点都与深度为 K 的满二叉树中编号从 1 至 n 的结点一一对应时称之为完全二叉树。注:满二叉树是一种特殊的完全二叉树。
前 k 层都是满的,最后一层不一定满,但最后一层从左到右必须是连续的。
深度为 k 的完全二叉树的节点个数最多为 2k - 1,最少为 2k-1 - 1 + 1(前k层节点个数总和+1,因为第k层至少有一个),所以节点个数范围是:[ 2k-1, 2k - 1 ]
2.3 二叉树的性质
1.若规定根节点的层数为1,则一棵非空二叉树的第 i 层上最多有 2i-1 个结点。
2.若规定根节点的层数为1,则**高度(深度)为 h 的二叉树的「最大结点数」**是 2h - 1。
3.对任何一棵二叉树,如果度为 0 的叶结点个数为 n0,度为 2 的分支结点个数为 n2,则有 n0=n2 +1(度为 0 的节点 比 度为 2 的节点 多一个)
4.若规定根节点的层数为 1,具有 n 个结点的「满二叉树」的高度(深度) h = log2(n+1)。 (log以2为底,n+1为对数)
5.对于具有 n 个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从 0 开始编号,则对于序号为 i 的结点有:
若 i > 0,i 位置节点的双亲序号:(i - 1) / 2;i=0,i 为根节点编号,无双亲节点
若 2i + 1 < n,左孩子序号:2i + 1,2i + 1 >= n否则无左孩子
若 2i + 2 < n,右孩子序号:2i + 2,2i + 2 >= n否则无右孩子
2.4 二叉树的存储结构
二叉树一般可以使用两种结构来存储,一种顺序结构,一种链式结构。
顺序存储
顺序存储就是使用数组来存储,而「数组」一般只适合表示「满二叉树」或「完全二叉树」,因为不是完全二叉树会有「空间的浪费」。在实际使用中,只有「堆」才会使用数组来存储。二叉树的顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。
在数组中用下标来表示树中的父子关系,满足以下关系:
leftchild = parent * 2 + 1
rightchild = parent * 2 + 2
parent = (child - 1) / 2
链式存储
二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素间的逻辑关系。通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来记录该结点左孩子和右孩子所在的链结点的存储地址。链式结构又分为二叉链和三叉链,目前我们学的一般都是二叉链(红黑树等才会用到三叉链)
typedef int BTDataType;
// 二叉链
struct BinaryTreeNode
{
BTDataType data; // 数据域
struct BinaryTreeNode* leftchild; // 指向当前节点的左孩子
struct BinaryTreeNode* rightchild; // 指向当前节点的右孩子
};
// 三叉链
struct BinaryTreeNode
{
BTDataType data; // 数据域
struct BinaryTreeNode* leftchild; // 指向当前节点的左孩子
struct BinaryTreeNode* rightchild; // 指向当前节点的右孩子
struct BinaryTreeNode* parent; // 指向当前节点的双亲
};
来源:https://blog.csdn.net/weixin_48025315/article/details/123147569


猜你喜欢
- 从这章开始,会介绍几个常用的函数式接口工具,首先先来看下这个大家族:首先从Function接口开始介绍一. 概述该接口顾名思义,函数的意思,
- 现如今,验证码在Android的客户端还是非常普遍的.通过手机账号和验证码直接去注册应用账户的信息.很多应用都以这种方式来完成注
- 1.让方法返回多个参数1.1在方法体外定义变量保存结果using System; using System.Collections
- 前言在我们做后端服务Dao层开发,特别是大数据批量插入的时候,这时候普通的ORM框架(Mybatis、hibernate、JPA)就无法满足
- 函数指针函数指针是指向函数的指针变量。通常我们说的指针变量是指向一个整型变、字符型或数组等变量,而函数指针是指向函数。函数指针可以像一般函数
- 字符串和列表学完,自己试着写了一个非常简单的Python名片管理系统。新萌尝试,大佬们不要喷。修改名片的功能我偷了个懒,因为我不知道怎么通过
- 背景实际开发中,常常需要将比较复杂的 JSON 字符串转换为对应的 Java 对象。这里记录下解决方案。如下所示,是入侵事件检测得到的 JS
- 甲:听说最近java跌落神坛,python称霸武林了,你知道吗?乙:不是吧,我前几天看python怎么还是第三?丙:你们都在扯蛋,pytho
- 前言在我们的日常的编程当中,并发是始终离不开的主题,而在并发多线程当中,线程池又是一个不可规避的问题。多线程可以提高我们并发程序的效率,可以
- 本文实例为大家分享了java实现顶一下踩一下功能的具体代码,供大家参考,具体内容如下效果图如下:主页面index.html:<!DOC
- 本文实例讲述了Android string.xml中的替换方法。分享给大家供大家参考,具体如下:在android的开发中,经常会遇见一句话,
- 某少年宫引进了一批机器人小车。可以接受预先输入的指令,按指令行动。小车的基本动作很简单,只有3种:左转(记为L),右转(记为R),向前走若干
- 一、BroadcastState 的介绍广播状态(Broadcast State)是 Operator State 的一种特殊类型。如果我们
- 预览效果图:需要权限:<uses-permission android:name="com.android.launcher
- 适用场合: 7.3工厂模式的适用场合 创建新对象最简单的办法是使用new关键字和具体类。只有在某些场合下,创建和维护对象工厂所带来的额外复杂
- 使用maven引入jar<dependency> <groupId>com.itextpdf</g
- 摘要今天用compose来构建一个气泡上升粘连动画和水滴下坠动画,Github源码点击这里知识点compose动画贝塞尔曲线缓动函数comp
- public string NextString(int charLowerBound, int charUpperBound, int l
- 本文介绍通过Java程序批量替换PDF中的指定文本内容。程序环境准备如下:程序使用环境如图,需要注意的是,本文使用了免费版的PDF jar工
- 实现说明这里的核心在于如何在大并发的情况下保证数据库能扛得住压力,因为大并发的瓶颈在于数据库。如果用户的请求直接从前端传到数据库,显然,数据