当前位置: 首页 > news >正文

C数据结构:二叉树

目录

二叉树的数据结构

前序遍历

中序遍历

后序遍历

二叉树的创建

二叉树的销毁

二叉树的节点个数

二叉树叶子节点个数

二叉树第K层节点个数

二叉树的查找

层序遍历

判断二叉树是否为完全二叉树

完整代码


二叉树的数据结构

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

这里二叉树是使用了链式存储

data负责存储数据,left指针负责存储左孩子节点,right负责存储右孩子节点

前序遍历

void BinaryTreePrevOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}printf("%d ", root->_data);BinaryTreePrevOrder(root->_left);BinaryTreePrevOrder(root->_right);
}

前序遍历的规则是先访问根节点,然后访问左子树,最后访问右子树

根据规则我们在遍历前先打印当前根节点的值(若是遇到NULL则打印N代替空节点),然后再往下递归左子树、右子树即可 

中序遍历

void BinaryTreeInOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}BinaryTreeInOrder(root->_left);printf("%d ", root->_data);BinaryTreeInOrder(root->_right);
}

中序遍历的规则是先访问左子树,然后访问根节点,最后访问右子树

 所以这里先递归到左子树(最左边),然后打印当前节点的值,然后再访问右子树(右子树的左子树),打印值,循环往复

后序遍历

void BinaryTreePostOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}BinaryTreePostOrder(root->_left);BinaryTreePostOrder(root->_right);printf("%d ", root->_data);
}

 后序遍历的规则是先访问左子树,然后访问右子树,最后访问根节点

跟前面的前序遍历和中序遍历基本一致,都只是顺序不一样

二叉树的创建

BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{if (*pi >= n){(*pi)++;return NULL;}BTNode* root = (BTNode*)malloc(sizeof(BTNode));root->_data = a[(*pi)++];root->_left = BinaryTreeCreate(a, n, pi);root->_right = BinaryTreeCreate(a, n, pi);return root;
}

使用该函数需要传一个数组,并根据数组里的元素当作前序遍历构建起二叉树

并需要传一个数组的大小,和一个标记pi,在外面传参的时候从数组开始的下标开始传

这里为了让函数在递归过程中能够始终有一个变量能够指向数组的某个元素来迭代(遍历数组),不受递归变化而变化,所以需要一个指针变量,在下一次递归的时候只需要传地址,就能获得*pi的值,而不是通过传参

 在每次递归过程中需要先创建一个根节点root并开辟一块空间,并为该节点data赋值为当前走到的*pi的位置

接下来就要开始递归先从根的左子树开始到右子树,并分别赋值给root的左和右

这里的递归会一直递归,我们需要让他递归到空为止,所以需要设定if语句限定递归次数

if语句限定了当*pi超过了数组的长度或等于数组长度时,则不能继续递归获取空间

所以这里是从最后一个节点开始往回返,先return最后一个节点给上一个递归的左或者右节点,依次往复即可

二叉树的销毁

void BinaryTreeDestory(BTNode* root)
{if (root == NULL)return;BinaryTreeDestory(root);BinaryTreeDestory(root);free(root);
}

销毁我们需要用后序遍历,因为前序和中序会先将根节点给销毁了,这样就不好往下找了,故此要使用后序遍历来一个个free掉每一个节点

二叉树的节点个数

int BinaryTreeSize(BTNode* root)
{if (root == NULL)return 0;return BinaryTreeSize(root->_left)+ BinaryTreeSize(root->_right) + 1;
}

控制好递归的次数 

只需要在遍历每一层节点时+1,即可

二叉树叶子节点个数

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);
}

还是先控制递归的层数,当root等于空时往回返

若root的左和右都为空,则说明当前节点为叶子节点,返回1

最后返回左子树叶子节点和右子树叶子节点个数相加即可

二叉树第K层节点个数

int BinaryTreeLevelKSize(BTNode* root, int k)
{if (root == NULL)return 0;if (k == 1)return 1;return BinaryTreeLevelKSize(root->_left, k - 1) + BinaryTreeLevelKSize(root->_right, k - 1);
}

 和前面的递归思路都差不多

这里返回1的条件是k == 1,因为只需要递归k-1次即可,当递归次数达到说明也到了第k层,则返回1即可

最后返回给外层的时候把左右子树的结果返回起来相加即可

二叉树的查找

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;
}

 第一个if语句控制层数

第二个判断查找结果,若找到则返回根节点root

找到的结果返回给下面的ret1指针或者ret2指针,通过这两个指针返回给最外层

层序遍历

void BinaryTreeLevelOrder(BTNode* root)
{Queue q;QueueInit(&q);if (root)QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);printf("%d ", front->_data);if (front->_left)QueuePush(&q, front->_left);if (front->_right)QueuePush(&q, front->_right);}QueueDestroy(&q);
}

层序遍历是一层一层从左到右遍历,那么我们就需要借助队列来完成(先进先出)

例如该图层序遍历后就是1,2,4,3,5,6

首先我们需要将根节点入队列

然后进入循环,我们循环的条件是队列为空则停止循环

所以我们还需要不断入栈

这里第一层已经入栈了,这时候就可以拿到这个节点,我把它叫做front

后面我们就可以打印这个front节点的data

然后就可以删除掉这个节点了,但是删除的同时需要把它的左右孩子带进来,但是左右孩子有可能为空,所以先判断,若不为空则push入队列,这样第二层就入队列了

接下来继续pop2和4(pop的同时打印),然后push2和4的左右孩子

 最后不要忘记将队列销毁,防止造成内存泄漏

判断二叉树是否为完全二叉树

int BinaryTreeComplete(BTNode* root)
{Queue q;QueueInit(&q);if (root)QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front == NULL)break;QueuePush(&q, front->_left);QueuePush(&q, front->_right);}while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front == NULL){QueueDestroy(&q);return 0;}}QueueDestroy(&q);return 1;
}

和前面层序遍历差不多,我们也需要借用一个队列来完成该函数

先入第一个根节点进队列,然后循环的将二叉树的全部节点按照层序遍历都push进去 

push到最后队列的头节点front总会碰到空节点,所以我们碰到空节点就break出去循环,这是第一个循环的主要作用

例如该二叉树,我们push2的时候就会把左节点3和右节点NULL给入队列,那么我们的front肯定会接收到NULL从而跳出循环

如上并不是一个完全二叉树,所以我们判断它不是完全二叉树的理由就是根据层序遍历它的空节点后面还有一个节点6

所以我们第二个循环的任务就是pop掉队列剩下的所有值,若pop的时候发现里面还有非空节点,则不为完全二叉树,return 0

例如我们在把NULL节点接收给front时,我们肯定会经过4这个节点,然后4这个节点就会把它的左NULL和右6两个节点入队列

所以我们最后在把队列全部出掉的时候肯定会碰到这个6,既然碰到了这个6就说明这不是一个完全二叉树

完整代码

#include"BinaryTree.h"
#include"Queue.h"BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{if (*pi >= n){(*pi)++;return NULL;}BTNode* root = (BTNode*)malloc(sizeof(BTNode));root->_data = a[(*pi)++];root->_left = BinaryTreeCreate(a, n, pi);root->_right = BinaryTreeCreate(a, n, pi);return root;
}void BinaryTreeDestory(BTNode* root)
{if (root == NULL)return;BinaryTreeDestory(root);BinaryTreeDestory(root);free(root);
}int BinaryTreeSize(BTNode* root)
{if (root == NULL)return 0;return BinaryTreeSize(root->_left)+ BinaryTreeSize(root->_right) + 1;
}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);
}int BinaryTreeLevelKSize(BTNode* root, int k)
{if (root == NULL)return 0;if (k == 1)return 1;return BinaryTreeLevelKSize(root->_left, k - 1) + BinaryTreeLevelKSize(root->_right, k - 1);
}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;
}void BinaryTreePrevOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}printf("%d ", root->_data);BinaryTreePrevOrder(root->_left);BinaryTreePrevOrder(root->_right);
}void BinaryTreeInOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}BinaryTreeInOrder(root->_left);printf("%d ", root->_data);BinaryTreeInOrder(root->_right);
}void BinaryTreePostOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}BinaryTreePostOrder(root->_left);BinaryTreePostOrder(root->_right);printf("%d ", root->_data);
}void BinaryTreeLevelOrder(BTNode* root)
{Queue q;QueueInit(&q);if (root)QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);printf("%d ", front->_data);if (front->_left)QueuePush(&q, front->_left);if (front->_right)QueuePush(&q, front->_right);}QueueDestroy(&q);
}int BinaryTreeComplete(BTNode* root)
{Queue q;QueueInit(&q);if (root)QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front == NULL)break;QueuePush(&q, front->_left);QueuePush(&q, front->_right);}while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front == NULL){QueueDestroy(&q);return 0;}}QueueDestroy(&q);return 1;
}

相关文章:

C数据结构:二叉树

目录 二叉树的数据结构 前序遍历 中序遍历 后序遍历 二叉树的创建 二叉树的销毁 二叉树的节点个数 二叉树叶子节点个数 二叉树第K层节点个数 二叉树的查找 层序遍历 判断二叉树是否为完全二叉树 完整代码 二叉树的数据结构 typedef char BTDataType; typedef str…...

使用Nginx作为反向代理实现MQTT内外网通信

使用Nginx作为反向代理实现MQTT内外网通信 步骤1: 安装Nginx 确保你的服务器上已安装Nginx。如果未安装,可以通过以下命令在Ubuntu上安装Nginx: sudo apt update sudo apt install nginx步骤2: 配置Nginx 编辑Nginx的配置文件,通常是/etc…...

SpringBoot 上传文件示例

示例效果&#xff1a; 前端代码&#xff1a; <html> <head><title>上传文件示例</title></head> <body> <h2>方式一&#xff1a;普通表单上传</h2> <form action"/admin/upload" method"post" enctyp…...

9.js函数

函数是js复杂数据类型的一种---可以理解为存放代码的盒子 用来帮助我们封装、复用、扩展以及调用代码的工具 函数的两个阶段 &#xff08;1&#xff09;声明函数&#xff08;理解为创造&#xff09; ——声明式声明 语法&#xff1a;function 函数名(参数){...代码} ——赋值时…...

关于数据库和数据表的基础SQL

目录 一. 数据库的基础SQL 1. 创建数据库 2. 查看当前有哪些数据库 3. 选中数据库 4. 删除数据库 5. 小结 二. 数据表的基础SQL 1. 创建数据表 2. 查看当前数据库中有哪些表 3. 查看指定表的详细情况(查看表的结构) 4. 删除表 5. 小结 一. 数据库的基础SQL 1. 创建…...

【C语言深度解剖】(14):结构体内存对齐(详细配图讲解)

&#x1f921;博客主页&#xff1a;醉竺 &#x1f970;本文专栏&#xff1a;《C语言深度解剖》 &#x1f63b;欢迎关注&#xff1a;感谢大家的点赞评论关注&#xff0c;祝您学有所成&#xff01; ✨✨&#x1f49c;&#x1f49b;想要学习更多C语言深度解剖点击专栏链接查看&…...

学习笔记:C语言的32个关键字

一、标准C语言的32个关键字 1、基本数据类型&#xff1a; signed unsigned char int float double short long void 2、构造数据类型&#xff1a; struct union enum 3、数据存储类别&#xff1a; auto static extern register 4、数据优化&#xff1a; const volatile 5、9条…...

嵌入式学习 (Day:27 IPC --- 进程间通信)

IPC 进程间通信 interprocess communicate &#xff08;即&#xff1a;进程间进行数据交换&#xff09; 三大类&#xff1a; 进程间通信的方式&#xff08;共8种&#xff09; 1、古老的通信方式&#xff08;Linux设计时就有的&#xff09; 无名管道 有名…...

Python考试复习--day2

1.出租车计费 mile,waitmap(int,input().split(,)) if mile<3:money13wait*1 elif mile>3 and mile<15:money13(mile-3)*2.3wait*1 else:money1312*2.3(mile-15)*2.3*(10.5)wait*1 print({:.0f}.format(money)) 【知识点1】&#xff1a; map() 函数 【知识点1】&…...

整理好了!2024年最常见 20 道 Redis面试题(九)

上一篇地址&#xff1a;整理好了&#xff01;2024年最常见 20 道 Redis面试题&#xff08;八&#xff09;-CSDN博客 十七、Redis 的过期策略有哪些&#xff1f; Redis 的过期策略主要有三种&#xff1a; 定时删除&#xff1a;当为一个键设置了过期时间后&#xff0c;Redis 会…...

IDEA使用Maven打包项目的所有的依赖

要使用 Maven 命令将 Spring Boot 项目的依赖打包到 lib 文件夹中&#xff0c;你可以在终端中运行以下命令&#xff1a; mvn dependency:copy-dependencies -DoutputDirectory./lib这个命令会将项目的所有依赖&#xff08;包括运行时依赖&#xff09;复制到当前目录的 lib 文件…...

【C++ 】学习问题及补充

一.自定义类型不初始化直接就赋值&#xff0c;比如string类会怎么样 vectr<string>里已经给每个string对象已经分配好空间&#xff0c;为什么不初始化再赋值会报错 在C中&#xff0c;std::string类是一个动态字符串类&#xff0c;它内部管理着一个字符数组&#xff0c;用…...

内存泄漏案例分享3-view的内存泄漏

案例3——view内存泄漏 前文提到&#xff0c;profile#Leaks视图无法展示非Activity、非Fragment的内存泄漏&#xff0c;换言之&#xff0c;除了Activity、Fragment的内存泄漏外&#xff0c;其他类的内存问题我们只能自己检索hprof文件查询了。 下面有一个极佳的view内存泄漏例子…...

红外超声波雷达测距

文章目录 一HC-SR04介绍1HC-SR04简介及工作原理 二用HAL库实现HC-SR04测量距离1STM32CubeMX配置2keil53代码的添加 三效果 一HC-SR04介绍 1HC-SR04简介及工作原理 超声波是振动频率高于20kHz的机械波。它具有频率高、波长短、绕射现象小、方向性好、能够成为射线而定向传播等…...

AIGC 008-IP-Adapter文本兼容图像提示适配器用于文本到图像扩散模型

AIGC 008-IP-Adapter文本兼容图像提示适配器用于文本到图像扩散模型&#xff01; 文章目录 0 论文工作1 论文方法2 效果 0 论文工作 这篇论文介绍了 IP-Adapter&#xff0c;一种 高效地将预训练的图像到图像转换模型适应到新领域 的方法。它通过在预训练模型的 输入端 添加一个…...

Java入门基础学习笔记50——ATM系统

1、项目演示&#xff1b; 2、项目技术实现&#xff1b; 1&#xff09;面向对象编程&#xff1a; 每个账户都是一个对象&#xff0c;所以要设计账户类Account&#xff0c;用于创建账户对象封装账户信息。ATM同样是一个对象&#xff0c;需要设计ATM类&#xff0c;代表ATM管理系…...

# linux 中使用 visudo 命令,怎么保存退出?

linux 中使用 visudo 命令&#xff0c;怎么保存退出&#xff1f; 在 visudo 中保存并退出的方法取决于您使用的文本编辑器。通常情况下&#xff0c;visudo 会使用 vim 或 vi 或 Nano 作为默认的文本编辑器。 1、使用 Vim 或 vi 编辑器&#xff1a; 按下 Esc 键退出编辑模式&…...

springboot项目,@Test写法 @Before @After

某文件示例 package cn.xxx.crm.boss;import cn.xxxx.crm.manager.mq.rabbit.AliyunCredentialsProvider; import com.rabbitmq.client.AMQP; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; im…...

vue3的核心API功能:computed()API使用

常规使用方法: 这样是常规使用方法. 另一种,可写计算属性的使用方法: 这样分别定义computed的get回调函数和set回调函数, 上面例子定义了plusOne.value的值为1, 那么这时候就走了computed的set回调函数,而没有走get回调函数. 当我们打印plusOne.value的值的时候,走的是get的…...

Bootstrap5

Bootstrap5-容器 容器是Bootstrap—个基本的构建块&#xff0c;它包含、填充和对齐给定设备或视口中的內容。 Bootstrap 需要一个容器元素来包裏网站的内容 我们可以使用以下两个容器类&#xff1a; .container 类用于固定宽度并支持响应式布局的容器。.container-fluid 类用…...

第19节 Node.js Express 框架

Express 是一个为Node.js设计的web开发框架&#xff0c;它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用&#xff0c;和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

vscode(仍待补充)

写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh&#xff1f; debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

Cinnamon修改面板小工具图标

Cinnamon开始菜单-CSDN博客 设置模块都是做好的&#xff0c;比GNOME简单得多&#xff01; 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词

Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵&#xff0c;其中每行&#xff0c;每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid&#xff0c;其中有多少个 3 3 的 “幻方” 子矩阵&am…...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…...

第八部分:阶段项目 6:构建 React 前端应用

现在&#xff0c;是时候将你学到的 React 基础知识付诸实践&#xff0c;构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段&#xff0c;你可以先使用模拟数据&#xff0c;或者如果你的后端 API&#xff08;阶段项目 5&#xff09;已经搭建好&#xff0c;可以直接连…...

Monorepo架构: Nx Cloud 扩展能力与缓存加速

借助 Nx Cloud 实现项目协同与加速构建 1 &#xff09; 缓存工作原理分析 在了解了本地缓存和远程缓存之后&#xff0c;我们来探究缓存是如何工作的。以计算文件的哈希串为例&#xff0c;若后续运行任务时文件哈希串未变&#xff0c;系统会直接使用对应的输出和制品文件。 2 …...

Python常用模块:time、os、shutil与flask初探

一、Flask初探 & PyCharm终端配置 目的: 快速搭建小型Web服务器以提供数据。 工具: 第三方Web框架 Flask (需 pip install flask 安装)。 安装 Flask: 建议: 使用 PyCharm 内置的 Terminal (模拟命令行) 进行安装,避免频繁切换。 PyCharm Terminal 配置建议: 打开 Py…...

aardio 自动识别验证码输入

技术尝试 上周在发学习日志时有网友提议“在网页上识别验证码”&#xff0c;于是尝试整合图像识别与网页自动化技术&#xff0c;完成了这套模拟登录流程。核心思路是&#xff1a;截图验证码→OCR识别→自动填充表单→提交并验证结果。 代码在这里 import soImage; import we…...