数据结构--二叉树

目录

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

1.1 前置说明

1.2 二叉树的遍历

1.2.1 前序、中序以及后序遍历

1.2.2 层序遍历及判断是否为完全二叉树

1.3 节点个数,叶子节点个数,第k层节点个数以及高度等

1.4 二叉树的创建和销毁


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


1.1 前置说明

        这时直接创建的二叉树,方便于各个接口函数的测试,当然你也可以选择1.4的方法直接创建。

typedef int BTDataType;
typedef struct BinaryTreeNode
{BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}TreeNode;TreeNode* BuyTreeNode(int x)
{TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));assert(node);node->data = x;node->left = NULL;node->right = NULL;return node;
}
TreeNode* CreateTree()
{TreeNode* node1 = BuyTreeNode(1);TreeNode* node2 = BuyTreeNode(2);TreeNode* node3 = BuyTreeNode(3);TreeNode* node4 = BuyTreeNode(4);TreeNode* node5 = BuyTreeNode(5);TreeNode* node6 = BuyTreeNode(6);TreeNode* node7 = BuyTreeNode(7);node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;node5->right = node7;return node1;
}

再看二叉树基本操作前,再回顾下二叉树的概念,二叉树是:
        1. 空树
        2. 非空:根节点,根节点的左子树、根节点的右子树组成的。

        从概念中可以看出,二叉树定义是递归式的,因此后序基本操作中基本都是按照该概念实现的。


1.2 二叉树的遍历


1.2.1 前序、中序以及后序遍历

         学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。

按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历:
        1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
        2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中间。
        3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。

下面主要分析前序递归遍历,中序与后序图解类似,可以自己画画看。

        (1.)**前序遍历**,也称为**深度优先遍历**。它从根节点开始,递归地访问左子树,然后访问右子树。 二叉树的前序遍历是按以下顺序访问节点的序列: 1. 根节点 2. 根节点的左子树 3. 根节点的右子树

        当左边递归到空时,会从叶子节点开始依次返回递归的结果,然后开始遍历右子树,递归迭代。
前序遍历递归图解:

前序遍历的代码:

void PrevOrder(TreeNode* root)
{if (root == NULL){printf("N ");return;}printf("%d ", root->data);PrevOrder(root->left);PrevOrder(root->right);
}

前序遍历结果为:1 2 3 4 5 6

        (2.)**中序遍历**。它从根节点开始,递归地访问左子树,然后访问当前节点,然后访问右子树。 二叉树的中序遍历是按以下顺序访问节点的序列: 1. 当前节点的左子树 2. 当前节点 3. 当前节点的右子树

中序遍历代码:

void InOrder(TreeNode* root)
{if (root == NULL){printf("N ");return;}InOrder(root->left);printf("%d ", root->data);InOrder(root->right);
}

中序遍历的结果:3 2 1 5 4 6

        (3.)**后序遍历**。它从根节点开始,递归地访问左子树,然后访问右子树,最后访问根节点。 二叉树的后序遍历是按以下顺序访问节点的序列: 1. 左子树的所有节点 2. 右子树的所有节点 3. 根节点

后序遍历代码:

void PostOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}PrevOrder(root->left);PrevOrder(root->right);printf("%d ", root->data);
}

前序遍历结果为:3 2 5 6 4 1


1.2.2 层序遍历及判断是否为完全二叉树

层序遍历(又称广度优先遍历):除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。

        实现层序遍历,我们可以用到队列,A进入队列,可以去到B和C的地址,让A出队就能取到A的数据,然后通过B可以取到D、通过C可以取到E,F,让他们依次入队出队,就能取到每一层 的节点,最后队列为空就结束

void LevelOrder(TreeNode* root)
{Queue q;QueueInit(&q);if (root)QueuePush(&q, root);int levelSize = 1;while (!QueueEmpty(&q)){// 一层一层出while (levelSize--){TreeNode* front = QueueFront(&q);QueuePop(&q);//这里pop掉了front也能取到,因为这只是pop掉了指向节点的指针//并没有真的把节点pop掉了printf("%d ", front->data);if (front->left)QueuePush(&q, front->left);if (front->right)QueuePush(&q, front->right);}printf("\n");levelSize = QueueSize(&q);}printf("\n");QueueDestroy(&q);
}

判断是否为完全二叉树

        在层序遍历的基础上,我们稍作修改就可以了。我们再出队列的过程中,如果遇到了NULL,那我们就break,然后去判断NULL的后面是否有数据,如果NULL的后面还有数据,那么这就不是一个完全二叉树。

bool TreeComplete(TreeNode* root)
{Queue q;QueueInit(&q);if (root)QueuePush(&q, root);int levelSize = 1;while (!QueueEmpty(&q)){TreeNode* front = QueueFront(&q);QueuePop(&q);if (front == NULL)break;QueuePush(&q, front->left);QueuePush(&q, front->right);}// 前面遇到空以后,后面还有非空就不是完全二叉树while (!QueueEmpty(&q)){TreeNode* front = QueueFront(&q);QueuePop(&q);if (front)//判断是否为NULL,不是NULL返回false{QueueDestroy(&q);return false;}}QueueDestroy(&q);return true;
}

1.3 节点个数,叶子节点个数,第k层节点个数以及高度等

接口函数

// 二叉树节点个数
int BinaryTreeSize(BTNode* root);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);

1.二叉树节点的个数

        我们用分治的思想,依次遍历左右两子树,如果不是空则+1

int TreeSize(TreeNode* root)
{return root == NULL ? 0 : TreeSize(root->left) +TreeSize(root->right) + 1;
}

2.叶子节点的个数

        叶子节点的特征就是,左孩子右孩子都为空,则视为叶子节点,分别遍历两个子树的叶子节点,是叶子节点就返回1。

int treeleafsize(TreeNode* root)
{//空返回0if (root == NULL)return 0;//是叶子返回1if ((root->left == NULL) &&(root->right == NULL))return 1;//非0也不是叶子,那继续往下找叶子//分治 叶子=左+右return treeleafsize(root->left) +treeleafsize(root->right);
}

3.第k层的节点个数

        同样的,这里我们也用分治的思想。我们引入了变量k,我们从第一层开始,如果k不等于1的话,我就进行k-1的操作,直到k=1就到达了指定层数,k=1那么节点数就+1,统计每次递归到k=1的节点,就得到了第k层的节点数。

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

4.二叉树查找值为x的节点

        节点的查找不再是简单的遍历,我们递归一次就要保存一次递归的节点,因为递归是返回给每次调用的函数本身,函数是不能存值的,因此我们需要一个变量保存。

TreeNode* findtree(TreeNode* root, int x)
{if (root == NULL){return NULL;}if (root->data==x){return root;}//保存左子树的结果TreeNode* ret=findtree(root->left,x);if (ret){return ret;} //保存右子树的结果TreeNode* ret1=findtree(root->right, x);if (ret1){return ret1;}
}

5. 二叉树的高度

要找到二叉树的高度,我们可以使用以下算法(同样是分治的思想):

        1. 从根节点开始。

        2. 如果节点没有子节点,那么它的高度为 0。

        3.分别遍历左右子树

        4. 如果节点有一个子节点,那么它的高度为 1 加上其子节点的高度。

        5. 如果节点有两个子节点,那么它的高度为 1 加上其子节点高度的最大值。

        6.比较左右子树的大小,大的那个为二叉树的高度,最后加上根节点,就得到了二叉树的高度。

int TreeHeight(TreeNode* root)
{if (root == NULL)return 0;return fmax(TreeHeight(root->left), TreeHeight(root->right)) + 1;
}

这里我们用到fmax函数进行比较,当然你也可以选择使用运算符进行比较。


1.4 二叉树的创建和销毁

1.二叉树的创建

        用先序,中序,后序的方式去直接创建二叉树,那么,知道先序的序列就用先序的序列去递归创建树,知道中序的序列就用中序的序列去递归创建树,知道后序的序列就用后序的序列去递归创建树

eg:用数组来存储

TreeNode* TreeCreate(char* a, int* pi)
{if (a[*pi] == '#'){(*pi)++;return NULL;}TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode));if (root == NULL){perror("malloc fail");exit(-1);}root->data = a[(*pi)++];root->left = TreeCreate(a, pi);root->right = TreeCreate(a, pi);return root;
}

2.二叉树的销毁

        关于二叉树的销毁,你可以以任意序列的去销毁二叉树

void destroy_tree(Node *root) {if (root == NULL) {return;}destroy_tree(root->left);destroy_tree(root->right);free(root);
}

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

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

相关文章

flask web开发学习之初识flask(三)

文章目录 一、flask扩展二、项目配置1. 直接配置2. 使用配置文件3. 使用环境变量4. 实例文件夹 三、flask命令四、模版和静态文件五、flask和mvc架构 一、flask扩展 flask扩展是指那些为Flask框架提供额外功能和特性的库。这些扩展通常遵循Flask的设计原则,易于集成…

如何在Word中简洁地插入代码

如何在Word中简洁地插入代码 背景: ​ 最近在一写一些论文或者报告的时候,需要将源代码放在论文的最后,有一个很头疼的问题,如果直接把代码从编辑器复制到word中,就变成了下面这个样子: 这有点丑陋啊&…

【快速应用开发】看看RedwoodJS

自我介绍 做一个简单介绍,酒架年近48 ,有20多年IT工作经历,目前在一家500强做企业架构.因为工作需要,另外也因为兴趣涉猎比较广,为了自己学习建立了三个博客,分别是【全球IT瞭望】,【…

大数据分析与应用实验任务十一

大数据分析与应用实验任务十一 实验目的 通过实验掌握spark Streaming相关对象的创建方法; 熟悉spark Streaming对文件流、套接字流和RDD队列流的数据接收处理方法; 熟悉spark Streaming的转换操作,包括无状态和有状态转换。 熟悉spark S…

极简模式,助力宏观数据监控

随着UWA GOT Online采样的参数越来越多样化,为了提升开发者的使用体验,我们最新推出了三种预设数据采集方案:极简模式、CPU模式、内存模式。该更新旨在降低多数据采集对数据准确性的干扰,同时也为大家提供更精准且有针对性的数据指…

mac苹果笔记本电脑如何强力删除卸载app软件?

苹果电脑怎样删除app?不是把app移到废纸篓就行了吗,十分简单呢! 其实不然,因为在Mac电脑上,删除应用程序只是删除了应用程序的主要组件。大多数时候,系统会有一个相当长的目录,包含所有与应用程…

vuepress-----22、其他评论方案

vuepress 支持评论 本文讲述 vuepress 站点如何集成评论系统,选型是 valineleancloud, 支持匿名评论,缺点是数据没有存储在自己手里。市面上也有其他的方案, 如 gitalk,vssue 等, 但需要用户登录 github 才能发表评论, 但 github 经常无法连接,导致体验…

搞定这些软件测试面试题,面试通过率提高百分之80!

十九、持续集成 19.1 jenkins ant jmeter svn 接口自动化测试? jenkins ant jmeter svn 环境搭建 原来这个环境是我这边搭建的, 主要是几个步骤, 第一 Jenkins 安装、第二,ant 安装、第三, jmeter 安装、第四, …

关于加密解密,加签验签那些事

面对MD5、SHA、DES、AES、RSA等等这些名词你是否有很多问号?这些名词都是什么?还有什么公钥加密、私钥解密、私钥加签、公钥验签。这些都什么鬼?或许在你日常工作没有听说过这些名词,但是一旦你要设计一个对外访问的接口&#xff…

API测试基础之http协议

http简介: http(超文本传输协议)是一个简单的请求-响应协议,它通常运行在TCP(传输控制协议)之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。请求和响应消息的头以ASCII码形式给出…

生成式人工智能笔记-AIGC笔记

生成式人工智能笔记-AIGC笔记 十多年前,人工智能还只是一个不被人看好的小众领域,但是现在,它却已经成了街头巷尾的热点谈资,几乎任何事情都可以和人工智能联系在一起。 人工智能包括基础层、技术层和应用层。 基础层是人工智能…

WebRTC AEC回声消除算法拆解

WebRTC AEC算法流程分析——时延估计(一) 其实,网上有很多类似资料,各个大厂研发不同应用场景设备的音频工程师基本都对其进行了拆解,有些闪烁其词,有些却很深奥,笔者随着对WebRTC了解的深入&a…

scripty妙用

在monorepo项目中,随着子模块增多, 每个子项目都需要配置各自的package.json,并且大同小异,为了进一步提高配置效率,引入了scripty,自己写脚本,直接就可以用哦 1、安装 npm install scripty --save-dev 2…

实现安装“自由化”!在Windows 11中如何绕过“您尝试安装的应用程序未通过微软验证”

这篇文章描述了如果你不能安装应用程序,而是当你在Windows 11中看到消息“您尝试安装的应用程序未通过微软验证”时该怎么办。完成这些步骤将取消你安装的应用程序必须经过Microsoft验证的要求。 使用设置应用程序 “设置”应用程序提供了绕过此警告消息的最简单方法,以便你…

基于JavaWeb+SSM+Vue马拉松报名系统微信小程序的设计和实现

基于JavaWebSSMVue马拉松报名系统微信小程序的设计和实现 源码获取入口Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 Lun文目录 1系统概述 1 1.1 研究背景 1 1.2研究目的 1 1.3系统设计思想 1 2相关技术 2 2.…

记录 | vscode设置自动换行

右上菜单栏 -> 查看 -> 打开自动换行 或者还有种方式,如下, 左下角小齿轮,点击设置 然后输入 Editor: Word Wrap ,把开关打开为 on

微信小程序 长按录音+录制视频

<view class"bigCircle" bindtouchstart"start" bindtouchend"stop"><view class"smallCircle {{startVedio?onVedio:}}"><text>{{startVedio?正在录音:长按录音}}</text></view> </view> <…

AWTK 串口屏开发(1) - Hello World

1. 功能 这个例子很简单&#xff0c;制作一个调节温度的界面。在这里例子中&#xff0c;模型&#xff08;也就是数据&#xff09;里只有一个温度变量&#xff1a; 变量名数据类型功能说明温度整数温度。范围 (0-100) 摄氏度 2. 创建项目 从模板创建项目&#xff0c;将 hmi/…

adb命令学习记录

1、 adb ( android debug bridge)安卓调试桥&#xff0c;用于完成电脑和手机之间的通信控制。 xcode来完成对于ios设备的操控&#xff0c;前提是有个mac电脑。 安卓系统是基于linux内核来进行开发的。 2、adb的安装: 本身 adb是 android SDK 其中自带的工具&#xff0c;用于完…

【无标题】从0到1 搭建一个vue3+Django项目

目录 一、后端项目python django二、前端项目vitevue3三、后端配置3.1 将路由指向app3.2 app下创建urls.py&#xff0c; 写入路由3.3 views写入test函数3.4 启动服务&#xff0c;访问路由 四、前端配置4.1 安装一些工具库及创建文件4.1.1 安装需要用的三方库4.1.2 创建文件 4.2…