二叉树及其链式结构

news2025/2/1 17:51:23

目录

一:树概念及结构

1.树的概念

2.树的相关概念

 3.树的表示

 二:二叉树的概念及结构

 1.概念

 2.特殊的二叉树

<1>. 满二叉树:

<2>. 完全二叉树:

3.二叉树的性质

4.二叉树的存储结构

<1>.顺序结构

<2>.链式结构

5.链式二叉树的实现

<1>:二叉树的前序遍历

<2>.二叉树的中序遍历

<3>.二叉树的后序遍历

<4>.二叉树的节点个数

<5>.二叉树的叶子节点的个数

<6>.二叉树的高度

<7>.二叉树第k层节点个数

<8>.二叉树查找值为x的节点

<9>.二叉树销毁

<10>.通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树


一:树概念及结构

1.树的概念


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

2.树的相关概念

80791be6c40b42ec962970dc92e6a84b.png


节点的度:一个节点含有的子树的个数称为该节点的度; 如上图: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)棵互不相交的树的集合称为森林;
   

 3.树的表示

双亲表示法,
孩子表示法、
孩子双亲表示法
孩子兄弟表示法(常用).......
typedef int DataType;
struct Node
{
struct Node* firstChild; // 第一个孩子结点
struct Node* pNextBrother; // 指向其下一个兄弟结点
DataType _data; // 结点中的数据域
};

 画图表示:

88e1bdb385a446c1acb8eee1a93fa344.png

 二:二叉树的概念及结构

 1.概念


一棵二叉树是结点的一个有限集合,该集合:
1. 或者为空

2. 由一个根节点加上两棵别称为左子树和右子树的二叉树组成

7ad74b0b75fe48ca84338a38a8b68361.png

<1>. 二叉树不存在度大于2的结点
<2>. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树
其可能存在的特殊情况:空树 ; 只有根节点 ; 只有左子树 ; 只有右子树 ; 左右子树均存在。
再图形中表示如下图:
ee289ea92c2849dc86ebaae184d1737e.png

 2.特殊的二叉树

<1>. 满二叉树:

一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为H,且结点总数是 2^ H - 1,则它就是满二叉树。

ffea04af5ead473fa77e60a7feeb8509.png

<2>. 完全二叉树:

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

 3.二叉树的性质


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

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

3. 对任何一棵二叉树, 如果度为0其叶结点个数为n0 , 度为2的分支结点个数为 ,则有n0= n2+1

9ca3db06a47a4f57a20ef76f35ac1764.png

4.若规定根节点的层数为1,具有n个结点的满二叉树的深度:

h = log2  (n+1) --- 以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否则无右孩子

4.二叉树的存储结构

<1>.顺序结构

顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空 间的浪费。而现实中使用中只有堆才会使用数组来存储。
二叉树顺 序存储在物理上是一个数组,在逻辑上是一颗二叉树。

a9814f1502e44b96a2fa23260dc79d03.png

<2>.链式结构


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

5.链式二叉树的实现


在此处,我们先手动创建一个二叉树,链式二叉树,可以开辟节点,然后自己进行链接:

//链式二叉树

typedef char BTDataType;

typedef struct BinaryTreeNode
{
    BTDataType _data;
    struct BinaryTreeNode* _left;
    struct BinaryTreeNode* _right;
}BTNode;

BTNode* BuyNode(BTDataType x)
{
    //开辟结点
    BTNode* node = (BTNode*)malloc(sizeof(BTNode));
    if (node == NULL)
    {
        perror("malloc fail\n");
        return NULL;
    }
    node->_data = x;
    node->_left = NULL;
    node->_right = NULL;

    return node;

}
BTNode* CreaBinaryTree()
{
    BTNode* node1 = BuyNode('a');
    BTNode* node2 = BuyNode('b');
    BTNode* node3 = BuyNode('c');
    BTNode* node4 = BuyNode('d');
    BTNode* node5 = BuyNode('e');
    BTNode* node6 = BuyNode('f');
    BTNode* node7 = BuyNode('g');
    BTNode* node8 = BuyNode('h');

    //进行链接 --- 二叉树
    node1->_left = node2;
    node1->_right = node3;
    node2->_left = node4;
    node2->_right = node5;
    node3->_left = node6;
    node3->_right = node7;
    node4->_left = node8;

    return node1;

}

<1>:二叉树的前序遍历


前序:根、左子树、右子树

void BinaryTreePrevOrder(BTNode* root);


在自己进行链接时,开辟了:a b c d e f g h 几个节点,其链接后的图形为:

d94ff0ff22af4adf88925727a1cdd187.png

代码为:

// 二叉树前序遍历 
//前序遍历 --- 根,左子树,右子树
void BinaryTreePrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	printf("%c ", root->_data);

	BinaryTreePrevOrder(root->_left);
	BinaryTreePrevOrder(root->_right);

}

程序运行成功后:

3b911e7d8735493aa22905c7bf2944e2.png

 画图验证:

1bc86b2972d3443893f2e3496292b0ef.png

在此处我们仅画出了,左半部分的递归展开图

画到了(a b d h N N N e N N ),右边部分由于空间不够原因,不在展开画出。

<2>.二叉树的中序遍历


中序:左子树、根、右子树

void BinaryTreeInOrder(BTNode* root);


代码为:

// 二叉树中序遍历
//中序遍历 --- 左子树,根,右子树
void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	BinaryTreeInOrder(root->_left);

	printf("%c ", root->_data);
	BinaryTreeInOrder(root->_right);

}

运行效果为:

画递归展开图表示:

<3>.二叉树的后序遍历


后序:左子树、右子树、根

void BinaryTreePostOrder(BTNode* root);


代码为:

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

}

运行效果为:

 画递归展开图表示:

尝试画简单的递归展开图 --- 在二叉树上画图表示出来:

<4>.二叉树的节点个数


void BinaryTreeSize(BTNode* root);


若节点为空,则返回0。

否则,返回左子树的节点个数 + 右子树的节点个数 + 1

代码为:

// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}

	return BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right) + 1;
}

运行效果为:

 画函数递归展开图:

<5>.二叉树的叶子节点的个数


叶子节点 --- 度为 0 的节点。

void BinaryTreeLeafSize(BTNode* root);


若节点为空,则返回 0 ;

若节点的( 左/右 )节点都为空,即该节点的度为 0 ,为叶子节点,返回1;

然后计算左子树和右子树的叶子节点个数。

代码为:

// 二叉树叶子节点个数
//叶子节点 --- 度为0的节点
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);
}

运行效果为:

 根据构建的 二叉树可知,该二叉树的叶子节点为 4 

<6>.二叉树的高度


int BTreeHeight(BTNode* root);

//存在领导不记事的情况


代码为:

//二叉树的高度
int BTreeHeight(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}

	//领导不记事,当树特别大的时候,会超出时间限制。 --- 应将所得数据保存下来
	//return  BTreeHeight(root->_left) > BTreeHeight(root->_right) ? BTreeHeight(root->_left) + 1 : BTreeHeight(root->_right) + 1;

	int LeftHeight = BTreeHeight(root->_left);
	int RightHeight = BTreeHeight(root->_right);
	return LeftHeight > RightHeight ? LeftHeight + 1 : RightHeight + 1;
	//加1加的是根节点
}

运行效果为:

通过观察可以得知,我们所创建的二叉树为4层,故二叉树的高度为4。

<7>.二叉树第k层节点个数


int BinaryTreeLevelKSize(BTNode* root, int k);

//我们需要通过第 k - 1 层,来判定第 k 层


代码为:

// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	assert(k>0);// k 值必须为大于0的有效值
	if (root == NULL)
	{
		return 0;
	}
	if (k == 1)
	{
		return 1;
	}
	return BinaryTreeLevelKSize(root->_left, k - 1) 
		+ BinaryTreeLevelKSize(root->_right, k - 1);
}

运行效果为:

 第三层有四个

 第四层有一个

画递归展开图进行分析:

<8>.二叉树查找值为x的节点


BTNode* BinaryTreeFind(BTNode* root, BTDataType x);


代码为:

// 二叉树查找值为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->_right, x);
	if (ret2)
	{
		return ret2;
	}
	return NULL;
}

运行效果为:

----  可以找到的情况

 ---- 不可以找到的情况

<9>.二叉树销毁


void BinaryTreeDestory(BTNode** root);


代码为:

// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{
	if ((*root) == NULL)
	{
		return;
	}
	BinaryTreeDestory(&(*root)->_left);//BTNode ** root 
	BinaryTreeDestory(&(*root)->_right);
	free(*root);
	//在此处进行置空(NULL),对外面没有作用
	//可以自己在函数调用后进行置空操作
}

运行结果为:

---- 程序正确结束

<10>.通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树


 BTNode* BinaryTreeCreate(BTDataType* a, int* pi);

// pi --- 数组下标

//若 a[*pi] == '#' ,数组下标 + 1 ,并且返回空


 代码为:

BTNode* BinaryTreeCreate(BTDataType* a, int* pi)
{
	if (a[*pi] == '#')
	{
		(*pi)++;//数组下标 +1
		return NULL;
	}

	BTNode* root = BuyNode(a[*pi]);//开辟节点
	(*pi)++;

	//进行链接
	root->_left = BinaryTreeCreate(a, pi);
	root->_right = BinaryTreeCreate(a, pi);

	return root;
}

 运行效果为:

 画图表示:

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

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

相关文章

win11 无法登录微软账户 终极解决方案

背景&#xff1a;win11突然无法登录微软账户&#xff0c;office无法激活&#xff0c;Edge里的微软账户也无法登录&#xff0c;反馈中心也无法打开等&#xff0c;有网络&#xff0c;浏览器可以访问微软并进行登录。 试过网上的网络配置&#xff08;SSL及TLS协议勾选&#xff09…

数学模型:Python实现线性规划

文章摘要&#xff1a;线性规划的Python实现。 参考书籍&#xff1a;数学建模算法与应用(第3版)司守奎 孙玺菁。 PS&#xff1a;只涉及了具体实现并不涉及底层理论。学习底层理论以及底层理论实现&#xff1a;可以参考1.最优化模型与算法——基于Python实现 渐令 粱锡军2.算法导…

vim复制,剪切觉得麻烦?今天就来教会你:vim复制和剪切教程

上次讲了vim移动光标的快捷键&#xff0c;今天我们来了解一下vim的复制多行的功能。 快速复制多行 第一步&#xff1a;将光标移动到复制的文本开始的地方&#xff0c;按下v进入可视模式&#xff0c;这里的v的意思就是visual的缩写&#xff0c;就是可视的意思。 第二步&#x…

DBSCAN 集群

目录 DBSCAN 集群 主要代码 DBSCAN 绘制结果中的集群 DBSCAN 集群 基于密度的空间聚类应用&#xff08;DBSCAN&#xff09;是一种基于密度的聚类算法&#xff0c;由Martin Ester等人在1996年提出。该算法在半径为ε的圆内找到数据点的相邻点&#xff0c;并将它们加入到同一…

Git原理详解+指令操作,带你快速掌握Git

一、相关概念 版本控制 什么是版本控制&#xff1f; 用于管理多人协同开发项目的技术对文件的版本控制&#xff0c;要对文件进行修改、提交等操作 版本控制的分类 1.本地版本控制 就是放在本地 2.集中版本控制 SVN 所有的版本数据都保存在服务器上&#xff0c;协同开发…

GDAL读取属性表值乱码解决方法

文章目录 方法1&#xff1a;方法2&#xff1a;1.通过在线网网站&#xff0c;查看编码2.参考C中gbk和utf8互相转换文章 gdal示例代码 方法1&#xff1a; 在网上查看的推荐方法&#xff08;C代码&#xff09;&#xff0c;但没解决我遇到的问题&#xff1b; CPLSetConfigOption(&…

SSM整合快速入门案例

文章目录 前言一、设计数据库表二、创建工程三、SSM技术整合四、功能模块开发五、接口测试总结 前言 为了巩固所学的知识&#xff0c;作者尝试着开始发布一些学习笔记类的博客&#xff0c;方便日后回顾。当然&#xff0c;如果能帮到一些萌新进行新技术的学习那也是极好的。作者…

Java网络开发(Tomcat)——登陆和注册功能 的 迭代升级 从Jsp到JavaScript + axios + vue 诸多bug 同步到异步

目录 引出前置工作vueaxiosresp0.vue版本的jsp模板1.导包--Json&#xff1a;pom.xml文件&#xff1a;2.新建一个专门用来处理响应的实体类ResData3.在axios中&#xff0c;所有响应必须是 resp.getWriter().write() 的方式&#xff0c;核心代码如下4.在jsp前端代码中导包&#x…

华为OD机试真题B卷 JavaScript 实现【查字典】,附详细解题思路

一、题目描述 输入一个单词前缀和一个字典&#xff0c;输出包含该前缀的单词。 二、输入描述 单词前缀字典长度字典。 字典是一个有序单词数组。 输入输出都是小写。 三、输出描述 所有包含该前缀的单词&#xff0c;多个单词换行输出。 若没有则返回-1。 四、解题思路…

SLAM十四讲——ch3实践

ch3的实践及避坑 一、ch3的总体步骤二、文件的执行1. 实践&#xff1a;Eigen2. 实践&#xff1a;Eigen几何模块3. 可视化演示 出现的问题 一、ch3的总体步骤 确保已经安装Eigen库&#xff0c;Eigen库是一个C开源线性代数库。 sudo apt-get install libeigen3-dev说明&#xf…

在 Compose 中实现缓存列表数据提升用户体验(Stale-while-revalidate)

前言 最近在利用业余时间使用 Compose 实现一个 Github APP 客户端。 对标的是 GSY 大佬使用多种不同语言框架实现的 Github APP。 在实现过程中发现一些问题&#xff0c;因为这个客户端的数据几乎全部来自于 Github API&#xff0c;所以 UI 渲染也极度依赖于请求到的数据。…

JAVA面向对象(三)

第三章 封装与继承 目录 第三章 封装与继承 1.1.封装 1.2.包 1.3.访问权限控制 1.4.static修饰符 1.4.1.成员变量 1.4.2.成员方法 1.4.3.代码块 总结 内容仅供学习交流&#xff0c;如有问题请留言或私信&#xff01;&#xff01;&#xff01;&#xff01;&#xff0…

java并发编程:ArrayBlockingQueue详解

文章目录 一、简介二、数据结构三、源码分析3.1 属性3.2 构造方法3.3 方法3.3.1 入队3.3.2 出队3.3.3 获取元素3.3.4 删除元素 四、总结 一、简介 ArrayBlockingQueue 顾名思义&#xff1a;基于数组的阻塞队列。数组是要指定长度的&#xff0c;所以使用 ArrayBlockingQueue 时…

在知乎逮到一个腾讯10年老测试,聊过之后收益良多...

老话说的好&#xff0c;这人呐&#xff0c;一单在某个领域鲜有敌手了&#xff0c;就会闲得蛋疼。前几天我在上班摸鱼刷知乎的时候认识了一位腾讯测试大佬&#xff0c;在腾讯工作了10年&#xff0c;因为本人天赋比较高&#xff0c;平时工作也兢兢业业&#xff0c;现在企业内有一…

Python基础知识讲解——main方法

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 估计很多人跟我一样初学python看代码的时候先找一下main()方法&#xff0c;从main往下看。 但事实上python中是没有你理解中的“main()”方法的。 言归正传 if name "main":可以看成是python程序的入口&a…

数据结构与算法系列之习题练习

&#x1f497; &#x1f497; 博客:小怡同学 &#x1f497; &#x1f497; 个人简介:编程小萌新 &#x1f497; &#x1f497; 如果博客对大家有用的话&#xff0c;请点赞关注再收藏 &#x1f31e; 力扣习题 括号匹配问题。用队列实现栈。用栈实现队列。设计循环队列。 有效的括…

【数据分析案例】深度分析超市零售商店数据--Python数据分析实战

前言 咳咳&#xff0c;又是好久不见~这不高考已经结束了 对python感兴趣的准大学生们&#xff0c;是打算好好玩几个月还是&#xff0c;继续研究学习python呢~ &#x1f928; 我呢 还是建议大家劳逸结合哈哈 先玩再学习~ 当然啦 最重要的还是看你们自己呀 不过我以上这些都不能…

[NOI2007] 调兵遣将

题目描述 我军截获的情报显示&#xff0c;敌军正在集结兵力试图向我军重要的军械研究所发起进攻。由于我军正处于多线作战的状态&#xff0c;无法抽调大批兵力前去支援&#xff0c;指挥部决定通过有效的战前部署来提高胜率&#xff0c;减少伤亡和损失。 该军械研究所的平面图…

网络安全自学笔记+学习路线+就业规划(超详细)

每天都有新闻报道描述着新技术对人们的生活和工作方式带来的巨大乃至压倒性影响。与此同时有关网络攻击和数据泄露的头条新闻也是日益频繁。 攻击者可谓无处不在&#xff1a;企业外部充斥着黑客、有组织的犯罪团体以及民族国家网络间谍&#xff0c;他们的能力和蛮横程度正日渐…

如何使虚拟机自动生成ip地址

一. 打开虚拟机并登录账号进入命令行界面输入指令&#xff1a; vi /etc/sysconfig/network-scripts/ifcfg-ens33 通过指令进入到下面的界面当中 点击键盘输入 "i" 进入编辑模式将文件修改为 文件当中的 BOOTPROTO可以将ip地址定义为自动生成类型或者静态指定类型其中…