实用指南:[数据结构]--二叉树详解(结尾附原码)

news/2025/11/29 8:07:04/文章来源:https://www.cnblogs.com/yangykaifa/p/19284920

什么是树

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

注意,树形结构间,子树之间不能有交集,否则就不是树形结构

非树形结构

结论:

⼦树是不相交的
除了根结点外,每个结点有且仅有⼀个⽗结点
⼀棵N个结点的树有N-1条边

树的相关结论

⽗结点/双亲结点:若⼀个结点含有⼦结点,则这个结点称为其⼦结点的⽗结点;如上图:A是B的⽗结点
⼦结点/孩⼦结点:⼀个结点含有的⼦树的根结点称为该结点的⼦结点; 如上图:B是A的孩⼦结点
结点的度:⼀个结点有⼏个孩⼦,他的度就是多少;⽐如A的度为6,F的度为2,K的度为0
树的度:⼀棵树中,最⼤的结点的度称为树的度; 如上图:树的度为 6
叶⼦结点/终端结点:度为 0 的结点称为叶结点; 如上图: BCHI... 等结点为叶结点
分⽀结点/⾮终端结点:度不为 0 的结点; 如上图: DEFG... 等结点为分⽀结点
兄弟结点:具有相同⽗结点的结点互称为兄弟结点(亲兄弟); 如上图: BC 是兄弟结点
结点的层次:从根开始定义起,根为第 1 层,根的⼦结点为第 2 层,以此类推;
树的⾼度或深度:树中结点的最⼤层次; 如上图:树的⾼度为 4
结点的祖先:从根到该结点所经分⽀上的所有结点;如上图: A 是所有结点的祖先
路径:⼀条从树中任意节点出发,沿⽗节点-⼦节点连接,达到任意节点的序列;⽐如A到Q的路径为:
A-E-J-Q;H到Q的路径H-D-A-E-J-Q
⼦孙:以某结点为根的⼦树中任⼀结点都称为该结点的⼦孙。如上图:所有结点都是A的⼦孙
森林:由 m(m>0) 棵互不相交的树的集合称为森林;

二叉树

概念与结构

在树形结构中,我们最常⽤的就是⼆叉树,⼀棵⼆叉树是结点的⼀个有限集合,该集合由⼀个根结点
加上两棵别称为左⼦树和右⼦树的⼆叉树组成或者为空。

其中只存在左子树或只存在右子树,或左右子树都存在都是可以的

从上图中,我们可以得出结论

1. ⼆叉树不存在度⼤于 2 的结点
2. ⼆叉树的⼦树有左右之分,次序不能颠倒,因此⼆叉树是有序树

现实中的二叉树

特殊的二叉树

满二叉树

⼀个⼆叉树,如果每⼀个层的结点数都达到最⼤值,则这个⼆叉树就是满⼆叉树。也就是说,如果⼀ 个⼆叉树的层数为 K ,且结点总数是 2k − 1 ,则它就是满⼆叉树。

完全二叉树

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

注意,假设二叉树的高度为h层,那么前h-1层的二叉树是满的,最后第h层,可满可不满,但节点都必须从左往右的

二叉树重要结论

根据满⼆叉树的特点可知:
1)若规定根结点的层数为 1 ,则⼀棵⾮空⼆叉树的第i层上最多有 2i−1 个结点
2)若规定根结点的层数为 1 ,则深度为 h 的⼆叉树的最⼤结点数是 2h − 13)若规定根结点的层数为 1 ,具有 n 个结点的满⼆叉树的深度 h = log2 (n + 1) ( log
以2为底, n+1 为对数)

二叉树很像我们高中学的,一个首元素为1,公比为2的等比数列,

上面结论也确实可以用等比数列的公式进行推导

二叉树的存储结构

⼆叉树⼀般可以使⽤两种结构存储,⼀种顺序结构,⼀种链式结构。

顺序结构

顺序结构存储就是使⽤数组来存储,⼀般使⽤数组只适合表⽰完全⼆叉树,因为不是完全⼆叉树会有空间的浪费,完全⼆叉树更适合使⽤顺序结构存储。
现实中我们通常把堆(⼀种⼆叉树)(下面会讲)使⽤顺序结构的数组来存储,需要注意的是这⾥的堆和操作系统 虚拟进程地址空间中的堆是两回事,⼀个是数据结构,⼀个是操作系统中管理内存的⼀块区域分段

链式结构

⼆叉树的链式存储结构是指,⽤链表来表⽰⼀棵⼆叉树,即⽤链来指⽰元素的逻辑关系。 通常的⽅法 是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别⽤来给出该结点左孩⼦和右孩 ⼦所在的链结点的存储地址
分为二叉链和三叉链,此处讲解二叉链

堆(实现顺序结构的二叉树)

此处若想了解,可以翻看我上一篇文章,专门进行了讲解

实现链式二叉树

创建二叉树基本结构

⽤链表来表⽰⼀棵⼆叉树,即⽤链来指⽰元素的逻辑关系。 通常的⽅法是链表中每个结点由三个域组 成,数据域和左右指针域,左右指针分别⽤来给出该结点左孩⼦和右孩⼦所在的链结点的存储地址 , 其结构如下:
typedef struct BinaryTree
{struct BinaryTree* left;//指向左节点的孩子struct BinaryTree* right;//指向右节点的孩子int val;
}BTree;

导入二叉树过于麻烦,这里我们手动创建一颗二叉树

BTree* ByNode(int x)
{BTree* newnode = (BTree *) malloc(sizeof(BTree));if (newnode == NULL){perror("malloc");exit(1);}newnode->val = x;newnode->left = NULL;newnode->right = NULL;return newnode;
}
BTree* node1 = ByNode(1);
BTree* node2 = ByNode(2);
BTree* node3 = ByNode(3);
BTree* node4 = ByNode(4);
BTree* node5 = ByNode(5);
BTree* node6 = ByNode(6);
node1->left = node2;
node1->right = node4;
node2->left = node3;
node4->left = node5;
node4->right = node6;

前序中序后序遍历

即访问顺序不同
前序遍历 :
根在前,从左往右,一棵树的根永远在左子树前面,左子树又永远在右子树前面 (先访问根)
中序遍历 :
根在中,左子树在前,从左往右,一棵树的左子树永远在根前面,根又永远在右子树前面(第二访问根)
后序遍历:
根在后,从左往右,一棵树的左子树永远在右子树前面,右子树永远在根(最后访问根)
以下代码都是基于该二叉树 (不是会进行说明)
前序遍历

代码实现(使用递归)

void frontroot(BTree* root)
{if (root == NULL){printf("N ");return;}printf("%d ",root->val);frontroot(root->left);//最大的左子树全部访问完,递归才会回到第一次由main函数进入到此函数的位置,然后去访问右子树frontroot(root->right);
}

流程图详解

函数递归栈帧图

如果还不是足够明白,可以去看看函数栈帧相关的知识

遍历结果 1  2   3   N   N  N   4   5  N N 6 N N    (N为NULL)

中序遍历

与前序一样,只是顺序不同

void midroot(BTree* root)
{if (root == NULL){printf("N ");return;}midroot(root->left);//最大的左子树全部访问完,递归才会回到第一次由main函数进入到此函数的位置,然后去访问根printf("%d ",root->val);midroot(root->right);
}

遍历结果:  N  3  N  2  N  1    N  5  N  4    N   6   N

后序遍历

与前面类似

void lastroot(BTree* root)
{if (root == NULL){printf("N ");return;}lastroot(root->left);//最大的左子树全部访问完,递归才会回到第一次由main函数进入到此函数的位置,然后去访问右子树lastroot(root->right);printf("%d ", root->val);
}

遍历结果: N   N   3   N   2   N   N  5    N   N  6   4  1

二叉树销毁

void BinaryTreeDestory(BTree** root)//改变指针的指向,传二级指针
{//使用后序遍历if (*root == NULL)return;BinaryTreeDestory((*root)->left));BinaryTreeDestory((*root)->right));*root = NULL;
}

层序遍历

除了先序遍历、中序遍历、后序遍历外,还可以对⼆叉树进⾏层序遍历。设⼆叉树的根结点所在层数 为1,层序遍历就是从所在⼆叉树的根结点出发,⾸先访问第⼀层的树根结点,然后从左到右访问第2 层上的结点,接着是第三层的结点,以此类推,⾃上⽽下,⾃左⾄右逐层访问树的结点的过程就是层序遍历
实现需要借助其他数据结构:队列(队列详细实现在前面已讲,此处直接使用
void LevelOrder(BTree* root)
{QE qe;QEInit(&qe);if(root)QEPush(&qe,root);//传的是指针,要将QEDateType这个重命名改一下while(QEEmptert(&qe)){BTree* froot = QEFront(&qe);QEPop(&qe);if (froot->left);QPush(&qe, root->left);if(froot->right)QPush(&qe,root->right)}
}
//队列先进先出,先进一个,再出一个,然后将出的这个头节点的孩子插入到队列中

其他相关操作

节点的个数

即访问每个节点再统计,此处采用前序遍历访问

int NodeNum2(BTree* root)
{//空返回0//不为空,返回左子树加右子树再加一if (root == NULL)return 0;return NodeNum2(root->left) + NodeNum2(root->right) + 1;
}

叶子个数

即该节点没有一个孩子的节点个数

int Treeleaf(BTree* root)
{if (root == NULL)return 0;if (root->left == NULL && root->right == NULL)return 1;return Treeleaf(root->left) + Treeleaf(root->right);
}

二叉树第k层节点个数

假设从上往下数有h层,第k层在h层内,那么每次调用k-1,当k==,即是第k层

int BTreeKSize(BTree*root,int k)
{if (root == NULL)return 0;if (k == 1){return 1;}return (BTreeKSize(root->left, k - 1) + BTreeKSize(root->right, k - 1));
}

二叉树的高度

可以先统计左子树高度,再统计右子树高度,比较哪个大取哪个,最后加上根节点1

int TreeHigh(BTree* root)
{//数据不多的情况下可以,多的话,左子树与右子树的个数没有存着,会多次调用,叶子最明显if (root == NULL)return 0;if (root->left == NULL && root->right == NULL)return 1;/*return (TreeHigh(root->left) > TreeHigh(root->right) ? TreeHigh(root->left) + 1 : TreeHigh(root->right) + 1);*///解决方法一:将左子树与右子树存起来TreeHigh(root->left) > TreeHigh(root->right)int left = TreeHigh(root->left);int right = TreeHigh(root->right);//如果left == right,则随机返回一个//return (left > right ? left + 1 : right + 1);//解决方法二;使用fmax函数return fmax(TreeHigh(root->left), TreeHigh(root->right)) + 1;//要使用头文件:math.h
}

⼆叉树查找值为x的结点

BTree* TreeXNode(BTree*root,int x)
{//前序遍历if (root == NULL)return NULL;if (root->val == x)return root;BTree * rootl =  TreeXNode(root->left, x);if (rootl)return rootl;BTree* rootr = TreeXNode(root->right, x);if (rootr)return rootr;//可改写为//return (TreeXNode(root->right, x));return NULL;
}

判断是不是完全二叉树

使用的方法与前面层序遍历类似

void LevelOrder(BTree* root)
{QE qe;QEInit(&qe);if(root)QEPush(&qe,root);//传的是指针,要将QEDateType这个重命名改一下while(QEEmptery(&qe){BTree* froot = QEFront(&qe);QEPop(&qe);if(front == NULL)//如果出的数据是空,那么就可以开始判断了break;if (froot->left);QPush(&qe, root->left);if(froot->right)QPush(&qe,root->right)}while(QEEmptery(&qe){BTree*front = QEFront(&qe);if(froot == NULL)return false;QEPop(&qe);}return true;
}

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

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

相关文章

合同管理系统厂商有哪些?国内主流服务商盘点

随着企业数字化转型加速,合同管理作为业务合规与风险控制的核心环节,其系统化、智能化需求日益凸显。目前国内市场上涌现出多家专注于合同生命周期管理(CLM)的服务商,不同厂商在技术架构、行业适配性及系统集成能…

合同管理系统推荐:企业全流程智能化管理方案盘点

合同管理系统作为企业数字化转型的重要支撑,覆盖合同起草、审批、签署、履约至归档的全生命周期,通过标准化流程与智能化技术优化管理环节,有效降低合规风险、提升运营效率,已成为现代企业提升核心竞争力的关键工具…

2025 年深圳摄影培训机构推荐榜:路人贾(深圳分公司)排名第一、摄影十杰创办

随着新媒体内容创作与电商视觉营销的爆发式增长,市场对具备高水准商业摄影技能人才的需求持续井喷。无论是希望实现职业转型、提升副业收入,还是摄影爱好者渴望进阶,选择一家专业、实战、体系化的摄影培训机构,成为…

2025年靠谱的自润滑纤维缠绕轴承厂家最新推荐排行榜

2025年靠谱的自润滑纤维缠绕轴承厂家推荐排行榜 行业背景与市场趋势 随着工业技术的不断进步,自润滑纤维缠绕轴承凭借其轻量化、耐磨损、免维护等优势,在航空航天、轨道交通、清洁能源、智能制造等领域得到广泛应用…

好用的SPC软件公司口碑推荐:哪些企业值得关注?

在现代制造业质量管理体系中,SPC(统计过程控制)软件通过对生产过程数据的实时采集、分析与监控,帮助企业及时发现质量波动、优化生产流程,是提升产品质量稳定性的重要工具。选择一家技术可靠、口碑良好的SPC软件公…

好用的SPC软件公司推荐:聚焦专业服务与技术实力

好用的SPC软件公司推荐:聚焦专业服务与技术实力在制造业数字化转型进程中,SPC(统计过程控制)软件作为实现质量数据实时分析、生产过程精准管控的关键工具,受到越来越多企业的重视。选择一款功能适配、技术成熟的S…

国内做水处理的公司有哪些?行业实力企业盘点

随着工业生产精细化和环保要求的提高,水处理技术已成为保障生产安全、推动绿色发展的关键环节。国内水处理行业近年来在技术研发和应用落地方面持续突破,众多企业凭借专业解决方案,为半导体、化工、医药等多领域提供…

办公室咖啡机推荐:提升办公效率的品质之选

在快节奏的办公环境中,一台合适的咖啡机不仅能为员工带来片刻的放松,更能提升团队的工作状态。选择一款操作便捷、口味多样且维护方便的办公室咖啡机,成为许多企业优化办公体验的重要考量。 一、推荐榜单 推荐 [1]:…

办公室咖啡机哪种好?热门品牌特点与口碑分析

在快节奏的办公环境中,一杯合心意的咖啡不仅能缓解工作疲劳,更能提升团队的幸福感与协作效率。选择一款适配办公场景的咖啡机,需要兼顾操作便捷性、口味多样性及日常维护的便捷度,让不同需求的员工都能轻松享受咖啡…

好用的SPC软件企业推荐:聚焦行业优质选择

在现代制造业中,SPC(统计过程控制)软件作为质量管控的关键工具,能够通过数据采集与分析实现生产过程的实时监控,助力企业提升产品一致性与稳定性。选择一款好用的SPC软件,对企业实现质量追溯、过程优化具有重要意…

2025年靠谱的收割机自润滑轴承厂家推荐及选购参考榜

2025年靠谱的收割机自润滑轴承厂家推荐及选购参考榜行业背景与市场趋势随着农业机械化水平的不断提升,收割机作为现代农业装备的重要组成部分,其性能与可靠性直接影响农业生产效率。在收割机的众多关键零部件中,轴承…

应用安全 --- 安卓安全 之 dex动态加载

应用安全 --- 安卓安全 之 dex动态加载应用使用了动态 DEX 加载技术 - 代码不是直接在 APK 中,而是运行时动态加载

2025年质量好的非金属自润滑轴承/液压传动自润滑轴承厂家最新权威推荐排行榜

2025年质量好的非金属自润滑轴承/液压传动自润滑轴承厂家权威推荐排行榜行业背景与市场趋势随着工业4.0时代的深入发展,非金属自润滑轴承和液压传动自润滑轴承作为关键基础零部件,在高端装备制造领域的重要性日益凸显…

2025年比较好的阻燃输送带/耐高温输送带厂家选购指南与推荐

2025年比较好的阻燃输送带/耐高温输送带厂家选购指南与推荐行业背景与市场趋势随着工业自动化水平的不断提升,输送带作为物料搬运系统的核心部件,在各行各业的应用日益广泛。特别是在钢铁、冶金、煤炭、水泥等高温、…

2025年质量好的输送带厂家最新推荐权威榜

2025年质量好的输送带厂家推荐权威榜行业背景与市场趋势随着全球工业化进程的不断推进,输送带作为物料输送系统的核心部件,在矿山、钢铁、水泥、港口、电力等多个行业中发挥着不可替代的作用。2025年,中国输送带市场…

2025年热门的锻铜雕塑厂家最新用户好评榜

2025年热门的锻铜雕塑厂家用户好评榜 开篇:行业背景与市场趋势 锻铜雕塑作为一种兼具艺术性与实用性的工艺品,近年来在公共艺术、园林景观、建筑装饰等领域的需求持续增长。随着城市化进程的加快和文旅产业的蓬勃发…

2025年质量好的异形雕塑/石雕雕塑TOP品牌厂家排行榜

2025年质量好的异形雕塑/石雕雕塑TOP品牌厂家排行榜行业背景与市场趋势随着城市化进程的加快和人们审美需求的提升,异形雕塑与石雕雕塑作为公共艺术和建筑装饰的重要组成部分,近年来市场需求持续增长。2025年,全球雕…

2025 年 11 月静音轮胎权威推荐榜:专业降噪技术与舒适驾乘体验口碑之选

2025 年 11 月静音轮胎权威推荐榜:专业降噪技术与舒适驾乘体验口碑之选 随着城市化进程加速和汽车保有量持续增长,车辆行驶过程中的噪音污染问题日益受到关注。静音轮胎作为汽车噪声控制的关键部件,其技术研发和市场…

2025年评价高的三体系认证公司/信息技术服务认证公司服务口碑榜

2025年评价高的三体系认证公司/信息技术服务认证公司服务口碑榜行业背景与市场趋势随着全球经济一体化进程加速和数字化转型深入推进,企业认证服务市场迎来了前所未有的发展机遇。2025年,三体系认证(ISO 9001质量管…

2025 年 11 月舒适轮胎品牌权威推荐榜:静音耐磨与极致驾乘体验的匠心之选

2025 年 11 月舒适轮胎品牌权威推荐榜:静音耐磨与极致驾乘体验的匠心之选 随着汽车消费升级和用户对驾乘体验要求的不断提高,舒适轮胎市场正迎来新一轮技术革新与品质升级。在当前的轮胎行业中,舒适性已从单一的性能…