数据结构第九讲:二叉树
数据结构第九讲:二叉树
- 1.实现链式结构二叉树
- 1.1二叉树的节点结构
- 1.2创建二叉树节点
- 1.3前中后序遍历
- 1.3.1前序遍历
- 1.3.2中序遍历
- 1.3.3后序遍历
- 1.3.4总结
- 1.4二叉树结点的个数
- 1.4.1错误示范
- 1.4.2实现方法
- 1.5二叉树叶子结点的个数
- 1.6二叉树第k层结点的个数
- 1.7二叉树的深度/高度
- 1.8二叉树查找值为x的结点
- 1.9二叉树的销毁
- 2.二叉树的层序遍历
- 2.1什么是层序遍历
- 2.2层序遍历的实现
- 2.2.1实现思路
- 2.2.2先创建一个队列
- 2.2.3代码的实现
- 3.判断是否为完全二叉树
- 3.1解题思路
- 3.2代码实现
这一讲我们要实现二叉树的链式结构,二叉树结构体中包含了数据、指向左孩子节点的指针和指向右孩子节点的指针,在这一讲中,我们将要体会的递归的暴力!!!
1.实现链式结构二叉树
1.1二叉树的节点结构
//二叉树的节点结构
typedef int BinaryTreeDataType;typedef struct BinaryTreeNode
{BinaryTreeDataType data;//保存数据struct BinaryTreeNode* left;//指向左孩子节点struct BinaryTreeNode* right;//指向右孩子节点
}BTNode;
1.2创建二叉树节点
也就是为二叉树创建节点,并将节点进行初始化
//创建二叉树节点
BTNode* BuyBTNode(int val)
{BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));if (newnode == NULL){perror("malloc fail!");return NULL;}newnode->data = val;newnode->left = newnode->right = NULL;return newnode;
}
1.3前中后序遍历
接下来我们要实现的是二叉树的遍历:
二叉树的遍历操作分为三种:前序遍历、中序遍历、后序遍历:
可以看出:这里区分的前中后其实就是根节点遍历的顺序
我们先总体看三种遍历的不同:
接下来我们来实现三种遍历,注意:三种遍历方法的代码实现非常简单,主要是思路的体会,三种方法都是使用的递归的思想:
1.3.1前序遍历
//前序遍历
//函数传入的是树的根节点
void PreOrder(BTNode* root)
{if (root == NULL){return;}//将节点的数据进行打印printf("%d ", root->data);PreOrder(root->left);PreOrder(root->right);
}
对于递归,return之后只是一个递归函数终止,然而形参的值不变,函数会继续向下执行,形成一个全新的递归函数:
1.3.2中序遍历
//中序遍历
void InOrder(BTNode* root)
{//也就是左根右进行遍历if (root == NULL){return;}//先对左边的数据进行读取,其实就是将左边的节点当成是根节点进行传入InOrder(root->left);//先打印根节点的值,然后再检查右节点是否为空printf("%d ", root->data);//当右节点不为空时,它会按照从上向下的顺序一直走到右节点的尽头//当然,当有右节点中存在左节点时,会先走左节点的循环,因为左节点的循环在上//而且会一下走到左节点的尽头,然后从下往上遍历左节点InOrder(root->right);
}
1.3.3后序遍历
//后序遍历
void PostOrder(BTNode* root)
{if (root == NULL){return;}//仍然是先走到最后一个左节点PostOrder(root->left);PostOrder(root->right);printf("%d ", root->data);
}
1.3.4总结
1.4二叉树结点的个数
1.4.1错误示范
根据我们之前所讲,那么我们应该会有一个初步的思路,我们先实现一下:
//二叉树结点个数
//先定义一个变量sz
int sz = 0;
int BTSize(BTNode* root)
{if (root == NULL){return 0;}//每循环一次,那么就将sz++一次++sz;BTSize(root->left);BTSize(root->right);return sz;
}
这时打印的结果也是非常beautiful啊,和预想的一样,但是,当我们这样时:
//二叉树结点个数
int size = BTSize(n1);
printf("%d ", size);//6
size = BTSize(n1);
printf("%d ", size);//12
我们就会惊奇地发现:结点的次数竟然会随着函数调用次数的增加而成倍地增长!原因就是使用了全局变量,全局变量在函数使用后不会销毁,那么我们就要进行更改了:
1.4.2实现方法
//二叉树结点个数
int BTSize(BTNode* root)
{if (root == NULL){return 0;}//返回左节点的个数和右节点的个数return 1 + BTSize(root->left) + BTSize(root->right);
}
1.5二叉树叶子结点的个数
//二叉树叶子结点个数
int BTLeafSize(BTNode* root)
{if (root == NULL){return 0;}if (root->left == NULL && root->right == NULL){return 1;}//这里的返回值也会参与加法运算,所以会呈现出累加的效果//也就是说,返回值会存储到BTLeafSize(root->left)或另一个函数中,然后再进入加法运算//返回左边的树的叶子节点个数 + 右边的树的叶子结点个数return BTLeafSize(root->left) + BTLeafSize(root->right);
}
对于递归,先搞清总体思路,如上边的:返回左边的树的叶子节点个数 + 右边的树的叶子结点个数,然后再想清楚结束条件,如:当左节点和右节点都为0时返回1,此时会发现递归已经写完了!!!
1.6二叉树第k层结点的个数
//二叉树第k层结点的个数
int BTLevelKSize(BTNode* root, int k)
{if (root == NULL){return 0;}//返回条件:当k = 1时if (k == 1){return 1;}//返回左边的二叉树第k层的节点个数和右边二叉树第k层的结点个数return BTLevelKSize(root->left, k - 1) + BTLevelKSize(root->right, k - 1);
}
1.7二叉树的深度/高度
要想在递归的过程中对数据逐渐进行增大,必须要让return返回的值被接收,而且参与递归运算,这里是创建变量进行存储,还可以使用递归函数进行存储,如:1+BTDeapth(root->right)(这里是瞎写,仅代表一个格式)
//二叉树的深度/高度
int BTDepth(BTNode* root)
{if (root == NULL){return 0;}int leftdepth = BTDepth(root->left);int rightdepth = BTDepth(root->right);//要想在递归的过程中对数据逐渐进行增大,必须要让return返回的值被接收,而且参与递归运算//这里是创建变量进行存储//还可以使用递归函数进行存储,如:1+BTDeapth(root->right)(这里是瞎写,仅代表一个格式)return leftdepth > rightdepth ? leftdepth + 1 : rightdepth + 1;
}
1.8二叉树查找值为x的结点
//二叉树查找值为x的结点
BTNode* BTFind(BTNode* root, BinaryTreeDataType x)
{if (root == NULL){return NULL;}//返回条件:当数据是我们想要的数据时,就进行返回if (root->data == x){return root;}BTNode* left = BTFind(root->left, x);if (left){//这里要十分注意的是://这里的return表示的是整个函数的返回,上面的return代表的是递归函数的返回//原因就在于使用了一个值来接受递归函数的返回值,使得递归函数结束递归了//如果这里不适用变量来接受的话,函数将会错误返回return left;}BTNode* right = BTFind(root->right, x);if (right){return right;}return NULL;
}
1.9二叉树的销毁
//二叉树的销毁
//因为销毁要改变指针的指针的指向,所以这里传的是二级指针
void BTDestory(BTNode** root)
{if (*root == NULL){return;}//这里的传参要注意:因为是二级指针接收,所以传入的应该是一级指针的地址//直接遍历所有结点,然后一一删除即可BTDestory(&(*root)->left);BTDestory(&(*root)->right);free(*root);*root = NULL;
}
2.二叉树的层序遍历
2.1什么是层序遍历
层序遍历就是按照层数,从左到右一次对数据进行遍历:
2.2层序遍历的实现
2.2.1实现思路
对于递归,它会一直执行下去,直到遇到结束条件,然而,这个算法中,并不支持递归的使用,因为我们想不出来什么结束条件能够成立,所以这时我们就想到了其它的方法:队列!!!
下面我们来看实现方法:
2.2.2先创建一个队列
恰巧我们刚刚学过了队列,所以我们完全可以将之前写的队列代码拿过来,但是要注意的是,之前我们所实现的队列中保存的值为int类型,但是现在因为插入的值为BTNode*类型,所以还要将类别进行更改:
//结点结构体
//尽管我们之前已经使用typedef将结构体的名字改变成了BTNode*
//这里仍然需要加上struct,否则编译器会识别不出来,万一是一个变量名呢对不对
typedef struct BTNode* QueueDataType;typedef struct QueueNode
{//和链表一样,也需要结点进行链接QueueDataType val;struct QueueNode* next;
}QueueNode;
2.2.3代码的实现
//二叉树层序遍历
void LevelOrder(BTNode* root)
{//先创建一个队列Queue q;//队列初始化Init(&q);//将二叉树链表入队列QueuePush(&q, root);//循环,当队列为空时结束循环,当队列不为空时进行循环while (!QueueEmpty(&q)){//将一个结点出队列BTNode* ret = QueueFront(&q);printf("%d ", ret->data);QueuePop(&q);//如果有左右结点的话,按顺序入队列if (ret->left){QueuePush(&q, ret->left);}if (ret->right){QueuePush(&q, ret->right);}}Destory(&q);
}
3.判断是否为完全二叉树
3.1解题思路
这一道题目仍然是应用队列的知识:
3.2代码实现
//判断是否为完全二叉树
bool BinaryTreeComplete(BTNode* n1)
{//先创建一个队列Queue q;Init(&q);//先将二叉树中的数据全部插入到队列中//先将对头元素插入到队列中QueuePush(&q, n1);while (!QueueEmpty(&q)){BTNode* top = QueueFront(&q);QueuePop(&q);if (top == NULL){break;}//如果top不为空,将top的左孩子结点和右孩子结点入队,这样就保障了NULL结点的入队QueuePush(&q, top->left);QueuePush(&q, top->right);}//入队完成之后,检查队列中的数据,不能够存在一个NULL数据while (!QueueEmpty(&q)){BTNode* top = QueueFront(&q);QueuePop(&q);//如果存在不为空的数据,返回falseif (top){Destory(&q);return false;}}Destory(&q);return true;
}
相关文章:

数据结构第九讲:二叉树
数据结构第九讲:二叉树 1.实现链式结构二叉树1.1二叉树的节点结构1.2创建二叉树节点1.3前中后序遍历1.3.1前序遍历1.3.2中序遍历1.3.3后序遍历1.3.4总结 1.4二叉树结点的个数1.4.1错误示范1.4.2实现方法 1.5二叉树叶子结点的个数1.6二叉树第k层结点的个数1.7二叉树的…...

英伟达推出B200A瞄准OEM客群,预估2025年高端GPU出货量年增55%
市场近日传出NVIDIA(英伟达)取消B100并转为B200A,据TrendForce集邦咨询了解,NVIDIA仍计划在2024年下半年推出B100及B200,供应CSPs(云端服务业者)客户,并另外规划降规版B200A给其他企…...
Codeforces Round 962 (Div. 3)-补题
A. Legs 二分答案,最后取左端点的值,保险起见,还是再验算一次 bool check(int x){int an/4;if(a*4(x-a)*2>n) return true;return false; }void solve(){cin>>n;int l0,rn;while(l1<r){int midlr>>1;if(check(mid)) rmid…...
pandas的文本与序列化
文章目录 1.pandas的文本与序列化 result_data pd.DataFrame(json_data_list)with open(jsonl_file_path, w, encodingutf-8) as jsonl_file:result_data.to_json(orientrecords, linesTrue, force_asciiFalse, path_or_bufjsonl_file)数据不换行 df.at[i, column_name_transc…...
在企业级环境中部署Java程序:Docker命令实用指南
在企业级环境中部署Java程序:Docker命令实用指南 引言 在企业级开发中,Java应用程序的部署往往需要考虑效率、安全性和可移植性。Docker作为一个流行的容器化平台,提供了一种简便、一致且可移植的方式来部署Java应用。以下是一些常用的Dock…...

LabVIEW远程开发
LabVIEW远程开发是指在不同地点的开发者通过网络协同工作,共同开发、调试和维护基于LabVIEW的应用程序。这种开发模式适用于分布式团队、远程办公和全球化项目合作,能够有效利用不同地区的人才和资源。以下是LabVIEW远程开发的详细介绍: 1. 远…...

工作随记:我在OL8.8部署oracle rac遇到的问题
文章目录 一、安装篇问题1:[INS-08101] Unexpected error while executing the action at state:supportedosCheck问题1解决办法:问题2:[INS-06003] Failed to setup passwordless SSH connectivity with thefollowing nodeis): [xxxx1, xxxx…...

C++:vector容器
概览 std::vector是C标准模板库(STL)中的一种动态数组容器。它提供了一种类似于数组的数据结构,但是具有动态大小和更安全的内存管理。 定义和基本特性 std::vector是C标准库中的一 个序列容器,它代表了能够动态改变大小的数组。与普通数组一样&#x…...
深入理解 AWS CodePipeline
AWS CodePipeline 是一种持续交付和持续集成(CI/CD)服务,用于自动化软件发布过程。它通过创建流水线来帮助你自动构建、测试和部署应用程序。以下是对 AWS CodePipeline 的深入理解,包括其工作原理、组件、功能和使用场景: 1. AWS CodePipeline 的基本概念 持续集成和持续…...

Qt:自定义钟表组件
使用QWidget绘制两种钟表组件,效果如下: 源码下载链接:GitHub - DengYong1988/Clock-Widget: Qt 自定义钟表组件 https://download.csdn.net/download/ouyangxiaozi/89616407 主要代码如下: ClockWgt.h #ifndef CLOCKWGT_H #d…...
前端性能优化-web资源加载优先级
前言 资源加载优先级是指在页面渲染的过程中,浏览器决定加载哪些资源并优先加载它们的一种机制。正确配置资源加载的优先级可以显著改善页面加载性能,确保关键资源优先加载,提高用户感知的加载速度。 Web 资源加载方式 同步加载 同步加载…...

Docker-数据卷指令
数据卷挂载修改内容...

Elasticsearch VS Typesense! Elasticsearch未来会被其它搜索引擎取代吗?
近期网上流行一批新的搜索引擎,动不动就大言不惭,要跟龙头老大Elasticsearch比,想把Elasticsearch击败。 1. Typesense 太猖狂了,对Elasticsearch极为不敬 如近期炒作很猖狂的Typesense开源搜索引擎,一出来就急着挑战…...

usb摄像头 按钮 静止按钮
usb摄像头 按钮 静止按钮 来分析一个UVC的摄像头的枚举信息 UVC学习:UVC中断端点介绍 https://www.eet-china.com/mp/a269529.html 输入命令lsusb -d 0c45:62f1 -v https://www.miaokee.com/705548.html >Video Class-Specific VS Video Input Header Descrip…...

SAP MM学习笔记 - 豆知识03 - 安全在库和最小安全在库,扩张物料的保管场所的几种方法,定义生产订单的默认入库保管场所,受注票中设定禁止贩卖某个物料
上一章讲了一些MM模块的豆知识。 - MR21 修改物料原价 - MM02 修改基本数量单位/评价Class - MMAM 修改物料类型/评价Class SAP MM学习笔记 - 豆知识02 - MR21 修改物料原价,MM02 修改基本数量单位/评价Class,MMAM 修改物料类型/评价Class-CSDN博客 …...

激光导航AGV叉车那么多,究竟该怎么选?一篇文章讲明白~
AGV叉车 随着经济的快速发展,大部分企业的物料搬运开始脱离人工劳作,取而代之的是以叉车为主的机械化搬运。AGV叉车是工业搬运车辆,是指对成件托盘货物进行装卸、堆垛和短距离运输作业的各种轮式搬运车辆,主要应用于货场、工厂车间…...

redis面试(七)初识lua加锁脚本
redisson redisson如何来进行redis分布式锁实现的源码,基于redis实现各种各样的分布式锁的原理 https://redisson.org/ 这是官网 https://github.com/redisson/redisson/wiki/Table-of-Content 这是官方文档 开始 demo 建一个普通的工程在pom.xml里引入依赖 <…...

企元数智百年营销史的精粹:借鉴历史创造未来商机
随着时代的发展和科技的进步,传统营销方式正在经历前所未有的颠覆和改变。在这个数字化时代,企业需要不断创新,同时借鉴百年营销史的精粹,汲取历史经验,创造未来商机。而"企元数智"作为现代营销的代表&#…...
Java @SpringBootTest注解用法
SpringBootTest 是 Spring Framework 中的一个注解,用于指示 Spring Boot 应用程序的测试类。当你在测试类上使用 SpringBootTest 注解时,Spring Boot 会启动一个 Spring 应用程序上下文,并且加载应用程序的 application.properties 或 appli…...

构建智能招聘平台:人才招聘系统源码开发指南
本篇文章,小编将详细探讨如何基于人才招聘系统源码开发一个智能招聘平台,为企业的人才战略提供支持。 一、智能招聘平台的核心功能 智能招聘平台的核心在于提高招聘效率和匹配度,这需要集成多个关键功能模块: 1.职位发布与管理…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...

css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...

python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...

PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...

阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)
cd /home 进入home盘 安装虚拟环境: 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境: virtualenv myenv 3、激活虚拟环境(激活环境可以在当前环境下安装包) source myenv/bin/activate 此时,终端…...

恶补电源:1.电桥
一、元器件的选择 搜索并选择电桥,再multisim中选择FWB,就有各种型号的电桥: 电桥是用来干嘛的呢? 它是一个由四个二极管搭成的“桥梁”形状的电路,用来把交流电(AC)变成直流电(DC)。…...
Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解
文章目录 1. 题目描述1.1 链表节点定义 2. 理解题目2.1 问题可视化2.2 核心挑战 3. 解法一:HashSet 标记访问法3.1 算法思路3.2 Java代码实现3.3 详细执行过程演示3.4 执行结果示例3.5 复杂度分析3.6 优缺点分析 4. 解法二:Floyd 快慢指针法(…...