数据结构之二叉树的精讲

𝙉𝙞𝙘𝙚!!👏🏻‧✧̣̥̇‧✦👏🏻‧✧̣̥̇‧✦ 👏🏻‧✧̣̥̇:Solitary_walk

      ⸝⋆   ━━━┓
     - 个性标签 - :来于“云”的“羽球人”。 Talk is cheap. Show me the code
┗━━━━━━━  ➴ ⷯ

本人座右铭 :   欲达高峰,必忍其痛;欲戴王冠,必承其重。

👑💎💎👑💎💎👑 
💎💎💎自💎💎💎
💎💎💎信💎💎💎
👑💎💎 💎💎👑    希望在看完我的此篇博客后可以对你有帮助哟

👑👑💎💎💎👑👑   此外,希望各位大佬们在看完后,可以互相支持,蟹蟹!
👑👑👑💎👑👑👑


对二叉树的基本概念性的理解,若有不明白的友友们,可以看一下前期写的关于堆与二叉树的精讲

链接在此:

有了大家对二叉树的基本理解接下来,对以下知识的理解应该是易如反掌!

1.二叉树的链式结构的实现

 对于任何一个二叉树的节点来说:都是由值域,左孩子,右孩子构成,只不过对于某些节点而言左右孩子为空

1.1定义一个二叉树的结构体
typedef int BT_DataType;
typedef struct BinaryTreeNode
{struct BinaryTreeNode* left; //左孩子struct BinaryTreeNode* right;//右孩子BT_DataType val;  //值域}BT;
1.2二叉树的链式结构

 为了方便对二叉树的理解,我暂时手动创建节点,进行连接

BT* BT_Create()
{BT* n1 = BuyNode( 1);BT* n2 = BuyNode( 2);BT* n3 = BuyNode( 3);BT* n4 = BuyNode( 4);BT* n5 = BuyNode(5);BT* n6 = BuyNode( 6);BT* n7 = BuyNode( 7);BT* n8= BuyNode( 8);BT* n9 = BuyNode( 9);BT* n10 = BuyNode( 10);n1->left = n2;n1->right = n3;n2->right = n4;n3->left = n5;n3->right = n6;n2->left = n7;n4->left = n8;return n1;
}
2.二叉树的遍历
2.1前序遍历(先序遍历)

先访问根节点,其次访问左子树,左后访问右子树,注意这是一个递归的定义。

见图如下:

 代码:
void Pre_order(BT* root)
{if (root == NULL){printf("%c ", 'n');//返回n表示为空return;}printf("%d ", root->val);Pre_order(root->left);Pre_order(root->right);
}
递归展开图的解释:

2.2中序遍历

先访问左子树,在访问根节点最后访问右子树,依然是一个递归的定义

分析如下:
 代码:
void In_Order(BT* root)
{if (root == NULL){printf("%c ", 'n');//返回n表示为空return;}In_Order(root->left);printf("%d ", root->val);In_Order(root->right);
}
2.3后续遍历

先访问左子树,再访问右子树,最后访问根节点,依然是递归定义

分析见下:

代码:
void Post_Order(BT* root)
{if (root == NULL){printf("%c ", 'n');//返回n表示为空return;}Post_Order(root->left);Post_Order(root->right);printf("%d ", root->val);}
3.与二叉树相关节点的求解
3.1求二叉树所有节点个数

可能有些老铁们会说:直接进行计数就可以了:只有是当前节点不为空就让计数器(size)加一,采用前序遍历的方法。没错也可以这样但是这样有些坑需要注意一下否则一不小心就掉进坑里了。

 

 这时候可能有老铁会说,直接定义一个全局变量不就OK了,注意当我们连续调用BT_Size()这个函数进行求个数的话会有问题滴!

 因为szie作为一个全局变量,第一调用确实为8没有错,但是当我们后续在进行调用的时候就需要把size 手动进行置零,(关于这个问题详解,感兴趣的友友们,可以看前期我写的博客:链接在此,自取,蟹蟹支持!)要不然只会在当前size = 8 的基础上进行相加,这也就是为什么最后会出现16,24的这样结果了,也就不以为奇了。

代码:
int size = 0;
int BT_Size(BT* root)
{//int size = 0;if (root == NULL)return size;size++;BT_Size(root->left);//对左子树进行个数的遍历BT_Size(root->right);//对右子树进行个数的遍历return size;
}

 运行结果:

3.2求二叉树叶节点个数

既然是让咱求叶节点个数,咱就需要知道什么是叶节点:度为0的节点(没有左孩子,也没有右孩子的节点)

通过前面对二叉树的遍历咱们应该渐渐对递归要有些体悟了,当一个问题很大的时候,可以化大为小,化繁为简。这样岂不是很爽!

 分析见下:
 代码:
int BT_Leaf_Size(BT* root)
{if (root == NULL)return 0;if (root->left == NULL && root->right == NULL) // 判断是否为叶节点return 1;return  BT_Leaf_Size(root->left) + BT_Leaf_Size(root->right);
}
3.3求二叉树第H层节点个数

假设根节点所在层次就是第一层,依次下推,第H层的每个节点必然是第(H-1)层节点的左右孩子,这不就解决问题了嘛:求二叉树第H层节点个数转化为求二叉树第H-1层每个节点的左右孩子之和不就OK了。

 分析见下:

 代码:
int BT_LevelH_Size(BT* root, int h)
{if (root == NULL)return 0;if (h == 1)return 1;
return BT_LevelH_Size(root->left, h - 1)+ BT_LevelH_Size(root->right, h - 1) ;
}
4.二叉树高度的求解

对于一个二叉树而言,树的高度是左子树与右子树相比高度最大的一个再加1

还是如此,借助递归思想

子问题:左子树与右子树相比高度最大的一个再加1

结束条件:判断当前节点是否为空,其次当前节点是否为叶节点

 分析见下:

代码:
int BT_Height(BT* root)// 求树高度
{if (root == NULL)return 0;if (root->left == NULL && root->right == NULL)return 1;int left_h = BT_Height(root->left);int right_h = BT_Height(root->right);return left_h > right_h ? left_h + 1 : right_h + 1;
}
5.二叉树的销毁

注意这是链式二叉树,不能直接进行删除,更为直接的是采用后续遍历来进行销毁

代码:

void BT_Destroy(BT* root)
{if (root == NULL)return;BT_Destroy(root->left);BT_Destroy(root->right);free(root);
}
6.二叉树的层次遍历

 对于二叉树的层次遍历很显然就是一层一层进行遍历,在这里借助队的性质先进先出,来实现对二叉树的层次遍历

当队头元素出队的时候同时让队头元素的左右孩子节点也进队

 这里需要借助咱们之前写的队列的相关代码才可以玩哟!

 代码:
void Level_order(BT* root)
{Queque q;QuqueInit(&q);QuquePush(&q, root);while (!QuequeEmpty(&q)){BT* front = QuequeFront(&q);//取队头元素if (front)printf("%d ", front->val);QuquePop(&q);//删除队头元素//队头元素的左右孩子进队if (front)  {QuquePush(&q, front->left);QuquePush(&q, front->right);}}QuqueDestroy(&q);
}
7.借助二叉树前序遍历的结果实现对二叉树的构建

 分析: 还是分治的思想,层层递归,直到遇到空返回,把每一个节点看作一个新的根节点,只要当前节点不为空,就继续先序遍历

首先这是一个IO型的,所以完全需要自己手撕代码,

先把当前字符串的内容进行接收

其次利用递归:先序建立二叉树

子问题:先序建立    结束条件:遇到空,直接返回

最后利用递归写一个中序遍历

 辅助:需要我们每一个数据开辟节点

 定义一个二叉树的结构体:

 递归建立二叉树:

注意这里必须是 (*pi)++,不能是 *pi++。因为是递归调用每一次都需要进行栈帧建立,这样就不能做保证下标正确性,问题本质同二叉树求节点个数中的size

 完整代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>typedef char BT_DataType; // 存储char类型数据
typedef struct BinaryTreeNode
{struct BinaryTreeNode* left;struct BinaryTreeNode* right;BT_DataType val;}BT;
BT* BuyNode( BT_DataType x)
{BT*  n1 = (BT*)malloc(sizeof(BT));if (n1 == NULL)return NULL;n1->val = x;n1->left = n1->right = NULL;return n1;
}
BT* CreateTree(char*pa,int* pi){if(pa[*pi] == '#'){(*pi)++; // err *(pi)++return NULL;}BT*root = BuyNode(pa[*pi]);(*pi)++; // err *(pi)++root->left = CreateTree(pa,pi );root->right =CreateTree(pa,pi );return root;}void  In_Order(BT*root){if(root == NULL)return ;In_Order(root->left);printf("%c ",root->val);In_Order(root->right);}int main() 
{char a[100];scanf("%s",a);int i = 0;// 下标访问数组BT* root =  CreateTree(a,&i);In_Order(root);
}
8.判断一棵树是否为完全二叉树

 对于这个,可以借助层次遍历的思想来玩。只要是在出队的时候连续全部为空即为完全二叉树;否则就不是完全二叉树

 

代码:

bool BT_Complete(BT* root)
{Queque q;QuqueInit(&q);QuquePush(&q, root);while (!QuequeEmpty(&q)){BT* front = QuequeFront(&q);//取队头元素QuquePop(&q);//删除队头元素if (front == NULL)break;QuquePush(&q, front->left);QuquePush(&q, front->right);}//来到这只能有2种情况:队列为空  front == NULLwhile (!QuequeEmpty(&q)){//只能是front为空BT* front1 = QuequeFront(&q);//取队头元素if(front1)return false;  //非空 说明不是二叉树QuquePop(&q);}QuqueDestroy(&q);return true;
}
9:二叉树的查找

查找某个节点的值是否存在,若存在则返回该节点的地址,否则返回NULL

可以用先序来遍历

 错误示例:当我想查找3这个节点时候

 相信有不少老铁们一开始就会这样写吧,但是明明3这个节点存在为什么会没有找到呢?

其实通过我们调试发现这样写的逻辑是有Bug的,及时当要查找的节点存在时,也会造成把已找到的节点覆盖掉,其实这个查找逻辑的代码重在对return 返回的考察

正确代码:
BT* BT_Find(BT* root, BT_DataType x) // 3
{if (root == NULL)return  NULL;if (root->val == x)return root; //先去左子树查找BT* left = BT_Find(root->left, x);if (left)return left;//说明左子树不存在,去右子树查找BT* right = BT_Find(root->right, x);if (right)return right;//来到这说明左右子树都不存在return NULL;
}

 运行结果:

10.二叉树相关OJ的练习
10.1 单值二叉树

 解题分析:

其实一个遍历就直接搞定了。拿双亲节点的值与孩子节点对应的值进行比较,若是不相等直接 return false

是不是看着比较简单,但是写起来是有坑滴

 

 运行结果:

其实走读一遍代码大概就找到问题所在了:return 语句返回是返回到调用当前函数的上一个函数里,并不是直接返回到最外层 

这个问题的本质同二叉树查找指定数据是一样的

OJ代码:
bool isUnivalTree(struct TreeNode* root) 
{if(root == NULL)return true;if(root->left){if(root->val != root->left->val)return false;}if(root->right){if(root->val != root->right->val)return false;}//递归左子树bool ret1 = isUnivalTree(root->left);//递归右子树bool ret2=  isUnivalTree(root->right);return ret1 && ret2;
}
10.2 判断2个二叉树是否相同

 解题分析:

采用遍历的方式,根节点与根节点进行对比,左孩子与左孩子对比,右孩子与右孩子对比,注意是对比val而不是对比节点所对应的地址

OJ代码:
bool isSameTree(struct TreeNode* p, struct TreeNode* q){if(p == NULL &&q == NULL)return true;if(p == NULL || q == NULL)return false;//来到这说明都不为空if(p->val  != q->val)return false;return  isSameTree(p->left,q->left) && isSameTree(p->right,q->right);//注意这里必须是 逻辑且的关系,不能是逻辑或
}

 OK,以上就是我今日要为大家分享的,希望各位都有得!对于二叉树这部分是数据结构初阶比较难的一部分了,初学的时候是不好理解,事事都有个过渡期,当然自己有空的时候也不要忘了敲敲代码!

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

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

相关文章

Python实现链表:从基础到应用

一、引言 链表是一种常见的数据结构&#xff0c;它由一系列节点组成&#xff0c;每个节点包含数据和指向下一个节点的指针。链表在内存中的存储不是连续的&#xff0c;这使得它在插入和删除操作上具有较高的效率。本文将使用Python语言来实现一个简单的链表&#xff0c;并展示其…

卷积神经网络基本概念补充

卷积&#xff08;convolution&#xff09;、通道&#xff08;channel&#xff09; 卷积核大小一般为奇数&#xff0c;有中心像素点&#xff0c;便于定位卷积核。 步长&#xff08;stride&#xff09;、填充&#xff08;padding&#xff09; 卷积核移动的步长&#xff08;stride…

小白提示您:FaceTime诈骗持续高发,小伙伴们谨防诈骗!

前几天小白的iPhone突然接到了个FaceTime通话请求&#xff0c;说是某抖音账号需要续费啥的才能解锁某些功能。&#xff08;具体小白也记不太清了&#xff09; 这几天也有朋友说有个支付宝客服打FaceTime通话给他说快递出现了点问题&#xff0c;需要操作认证一下才能退款啥的。…

多线程万字详解

进程和线程是计算机程序执行的两个重要概念。 1.进程&#xff1a; 进程是操作系统分配资源的基本单位&#xff0c;每个进程都有自己独立的地址空间&#xff0c;每启动一个进程&#xff0c;系统就会为它分配内存。进程间通信比较复杂&#xff0c;需要用到IPC&#xff08;InterP…

AttributeError_ ‘list‘ object has no attribute ‘view‘

问题描述 训练yolov9的时候遇到了下面的问题。 In loss_tal.py: pred_distri, pred_scores torch.cat([xi.view(feats[0].shape[0], self.no, -1) for xi in feats], 2).split( (self.reg_max * 4, self.nc), 1) The error is as follows&#xff1a; AttributeError: list …

JavaWeb之 Web概述

目录 前言1.1 Web和 JavaWeb的概念1.2 JavaWeb技术栈1.2.1 B/S架构1.2.2 静态资源1.2.3 动态资源1.2.4 数据库1.2.5 HTTP协议1.2.6 Web服务器 1.3 JavaWeb 学习内容 前言 博主将用 CSDN 记录 Java 后端开发学习之路上的经验&#xff0c;并将自己整理的编程经验和知识分享出来&a…

【Web自动化测试——代码篇十二】自动化测试模型——数据驱动测试和关键字驱动测试

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

理解C#里面的集合有哪些?怎么用,什么是安全集合?

介绍 在C#中&#xff0c;集合是一种用于存储和操作多个元素的数据结构。它们提供了各种操作&#xff0c;如添加、删除、查找等&#xff0c;以及遍历集合中的元素。集合通常根据其实现方式和行为特征进行分类。 集合继承IEnumerable 在C#中&#xff0c;几乎所有的集合类型都实现…

简历中自我评价,是否应该删掉?

你好&#xff0c;我是田哥 年后&#xff0c;不少朋友已经开始着手准备面试了&#xff0c;准备面试的第一个问题就是&#xff1a;简历。 写简历是需要一些技巧的&#xff0c;你的简历是要给面试官看&#xff0c;得多留点心。 很多简历上都会写自我评价/个人优势/个人总结等&…

2024有哪些免费的mac苹果电脑深度清理工具?CleanMyMac X

苹果电脑用户们&#xff0c;你们是否经常感到你们的Mac变得不再像刚拆封时那样迅速、流畅&#xff1f;可能是时候对你的苹果电脑进行一次深度清理了。在这个时刻&#xff0c;拥有一些高效的深度清理工具就显得尤为重要。今天&#xff0c;我将介绍几款优秀的苹果电脑深度清理工具…

一个Web3项目的收官之作,必然是友好的用户界面(Web3项目三实战之四)

正如标题所述,一个对用户体验友好的应用,总是会赢得用户大加赞赏,这是毋庸置疑的。 甭管是web2,亦或是已悄然而至的Web3,能有一个外观优美、用户体验效果佳的的界面,那么,这个应用无疑是个成功的案例。 诚然,Web3项目虽然核心是智能合约攥写,但用户界面也是一个DApp不…

【Leetcode每日一刷】哈希表|纲领、242.有效的字母异位词、349. 两个数组的交集

纲领 &#x1f517;代码随想录理论部分 关于哈希表这个数据结构就不再重复讲了&#xff0c;下面对几个关键点记录一下&#xff1a; 哈希碰撞 解决方法1&#xff1a;拉链法 解决方法2&#xff1a;线性探测法 下面针对做题要用到的三种结构讲一下&#xff08;也是重复造轮子了…

vue.config.js publicPath 和 vue-router base 结合配置项目根目录为二级目录案例

背景: 同个域名下需要有 PC 管理后台, H5 端, 企业微信 ......等多个端, 需要在一个域名下通过不同的路径来区分不同的项目; 例如: abc.com/pc, abc.com/h5, abc.com/wx-work.... 此处做个记录 步骤: 1. 修改 vue.config.js 中的 publicPath module.exports {outputDir:…

MATLAB|【免费】概率神经网络的分类预测--基于PNN的变压器故障诊断

目录 主要内容 部分代码 结果一览 下载链接 主要内容 ​《MATLAB神经网络43个案例分析》共有43章&#xff0c;内容涵盖常见的神经网络&#xff08;BP、RBF、SOM、Hopfield、Elman、LVQ、Kohonen、GRNN、NARX等&#xff09;以及相关智能算法&#xff08;SVM、决策…

Java 下载excel文件

一、背景 微信小程序需要导出excel文件&#xff0c;后端技术Java&#xff0c;前端使用uniapp框架&#xff0c;使用excel模板。 二、excel 报表模板 需要补充的内容是以下标记问号的&#xff0c;其中有个表格&#xff0c;内容是动态添加的 三、Java端代码实现 关键步骤&…

Topaz Video AI:一键提升视频品质,智能重塑影像魅力 mac/win版

Topaz Video AI是一款革命性的视频智能处理软件&#xff0c;它利用先进的机器学习和人工智能技术&#xff0c;为视频创作者提供了前所未有的视频增强和修复功能。无论您是专业视频编辑师、摄影师&#xff0c;还是热爱视频创作的爱好者&#xff0c;Topaz Video AI都能帮助您轻松…

Python程序的流程

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 年轻是我们唯一拥有权利去编制梦想的时…

【前端素材】推荐优质后台管理系统Annex平台模板(附源码)

一、需求分析 1、系统定义 后台管理系统是一种用于管理网站、应用程序或系统的管理界面&#xff0c;通常由管理员和工作人员使用。它提供了访问和控制网站或应用程序后台功能的工具和界面&#xff0c;使其能够管理用户、内容、数据和其他各种功能。 2、功能需求 后台管理系…

利用python爬取本站的所有博客链接

目录 前因 首先的尝试 解决办法 导入包 定义一个json配置文件 打开浏览器执行操作 注意 提取源代码并且进行筛选链接 执行结果 前因 由于自己要把csdn的博客同步到hugo中&#xff0c;把博客转为md格式已经搞好了&#xff0c;但是由于csdn的图片具有防盗链&#xff0c;…

2024年经典【自动化面试题】附答案

一、请描述一下自动化测试流程&#xff1f; 自动化测试流程一般可以分为以下七步&#xff1a; 编写自动化测试计划&#xff1b; 设计自动化测试用例&#xff1b; 编写自动化测试框架和脚本&#xff1b; 调试并维护脚本&#xff1b; 无人值守测试&#xff1b; 后期脚本维…