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

数据结构:二叉树的链式结构及相关算法详解

目录

一.链式结构的实现

1.二叉树结点基本结构,初始化与销毁:

二.链式结构二叉树的几种遍历算法

1.几种算法的简单区分:

2.前序遍历:

3.中序遍历:

4.后序遍历:

5.层序遍历(广度优先遍历BFS):

三.链式二叉树的几种使用

1.计算树的结点个数:

2.计算树的叶子结点个数:

3.计算树的第k层结点个数:

4.计算树的深度:

5.查找结点为X的结点:

6.判断二叉树是否为完全二叉树:


一.链式结构的实现

1.二叉树结点基本结构,初始化与销毁:

(1)⽤链表来表示⼀棵⼆叉树,即⽤链来指示元素的逻辑关系。通常的⽅法是链表中每个结点由三个域组成,数据域左右指针域,左右指针分别⽤来给出该结点左孩子和右孩子所在的链结点的存储地址, 其结构如下:

(2)二叉树结点的初始化与树的创建:

#include"Tree.h"
//结点的初始化
BTNode* buyNode(char x)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));node->data = x;node->left = node->right = NULL;return node;
}
//树结构的创建
BTNode* creatBinaryTree()
{BTNode* nodeA = buyNode('A');BTNode* nodeB = buyNode('B');BTNode* nodeC = buyNode('C');BTNode* nodeD = buyNode('D');BTNode* nodeE = buyNode('E');BTNode* nodeF = buyNode('F');nodeA->left = nodeB;nodeA->right = nodeC;nodeB->left = nodeD;nodeC->right = nodeE;nodeC->right = nodeF;
}

(3)链式二叉树的销毁:

(使用后序遍历,我在下文会写出具体操作)

void BinaryTreeDestory(BTNode** root)
{//这里因为要改变根节点,应该传入的是根节点的地址,所以得拿二级指针接收//递归出口if ((*root) == NULL){return;}//自叶向根方向的释放//如果先释放的话,就找不到叶子节点了BinaryTreeDestory(&((*root)->left));BinaryTreeDestory(&((*root)->right));free(*root);*root = NULL;
}


二.链式结构二叉树的几种遍历算法

1.几种算法的简单区分:

举一个普遍的例子具体说明一下,如图:

2.前序遍历:

简单说明一下前序遍历的基本逻辑:

假设一个树:   

    A
   / \
  B   C
 / \
D   E

那么其遍历过程为:
访问根节点  A 。
递归遍历左子树(以  B  为根):
访问  B 
递归遍历左子树(以  D  为根):
访问  D 
递归遍历右子树(以  E  为根):
访问  E 
递归遍历右子树(以  C  为根):
访问  C 

//前序遍历
void preOrder(BTNode* root)
{//头 左 右//递归出口if (root == NULL){printf("NULL");return;}printf("%c",root->data);preOrder(root->left);preOrder(root->right);
}

3.中序遍历:

void inOrder(BTNode* root)
{//左 头 右//递归出口if (root == NULL){printf("NULL");return;}inOrder(root->left); //注意别调用错了,调用中序的printf("%c", root->data);inOrder(root->right);
}

4.后序遍历:

void postOrder(BTNode* root)
{//递归出口if (root == NULL){printf("NULL");return;}postOrder(root->left);postOrder(root->right);printf("%c", root->data);
}

总结:如上述所示,前中后序遍历的代码共同点也是相当明显了,主要就是递归顺序左右子树的顺序不同而影响的代码输出顺序不同,其根本上来说就是函数栈帧的不断进行嵌套式的创建与销毁,如果挨个遍历可能会显得比较复杂,但通过代码所示的这种递归算法,前中后序的遍历实现也显得十分简洁明了

但还有一点尤其需要注意:就是不同于层序遍历,我上面所说的三种遍历都属于深度优先遍历(DFS),而层序遍历却属于广度优先遍历,关于这两大类遍历的优劣我会在实现层序遍历后作详细介绍

5.层序遍历(广度优先遍历BFS):

思路:

借助数据结构:队列,先通过根节点入队列,再循环判断队列是否为空,不为空则取队头然后出队头,并将队头结点的左右孩子入队列

(由于后续层序遍历的实现需要用到好些队列的知识,所有我先将队列的一些简单用法附在下面,需要的可以稍微看看)

//定义结点结构
typedef int QDataTpe;
typedef struct QueueNode
{struct QueneNode* next;QDataTpe data;
}QueueNode;//定义队列的结构
typedef struct Queue
{QueueNode* phead;//队头QueueNode* ptail;//队尾int size;
}Queue;BTNode* buyNode(char x);//队列初始化
void QueueInit(Queue* pq)
{assert(pq);pq->phead = pq->ptail = NULL;
}//入队(从队尾入)
void QueuePush(Queue* pq, QDataTpe x)
{assert(pq);//申请一个结点QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));if (newnode == NULL){perror("malloc failed");exit(1);}newnode->data = x;newnode->next = NULL;//队列为空,newnode是对头也是队尾if (pq->phead == NULL){pq->phead = pq->ptail = newnode;}else //队列非空,尾插{pq->ptail->next = newnode;pq->ptail = pq->ptail->next;}pq->size++;
}
//判断队列是否为空
bool QueueEmpty(Queue* pq)
{assert(pq);return pq->phead == 0;
}//出队(从队头出)
void QueuePop(Queue* pq)
{assert(!QueueEmpty(pq));//只有一个结点的情况下,要把队尾和队头的两个指针都考虑到if (pq->phead == pq->ptail){free(pq->ptail);pq->phead = pq->ptail = NULL;}QueueNode* next = pq->phead->next;free(pq->phead);pq->phead = next;  pq->size--;
}//取队头数据
QDataTpe QueueFront(Queue* pq)
{assert(!QueueEmpty(pq));return pq->phead->data;
}
//取队尾数据
QDataTpe QueueBack(Queue* pq)
{assert(!QueueEmpty(pq));return pq->ptail->data;
}//队列的销毁
void QueueDestory(Queue* pq)
{assert(pq);QueueNode* pcur = pq->phead;while (pcur){QueueNode* next = pcur->next;free(pcur);pcur = next;}pq->phead = pq->ptail = NULL;
}//取队列元素个数
int QueueSize(Queue* pq)
{return pq->size;
}
//层序遍历
void LeveIOrder(BTNode* root)
{Queue q;//创建一个队列QueueInit(&q);QueuePush(&q, root);while (!QueueEmpty(&q)){//取队头出队头,打印结点值BTNode* top = QueueFront(&q);QueuePop(&q);printf("%c", top->data);//将队头结点的非空左右孩子结点入队列if (top->left){QueuePush(&q, top->left);}if (top->right){QueuePush(&q, top->right);}QueuDestroy(&q);}}

小结: 
DFS 优点(深度优先遍历)
节点少时效率高,节省内存
适合需要“全探索”或“找到任意解即可”的场景(如迷宫路径)

 
DFS 缺点
可能陷入无限循环(需记录已访问节点)
不保证最短路径(除非剪枝优化)


BFS 优点 (广度优先遍历)
保证找到最短路径(无权图中)
层级分明,易于理解

 
BFS 缺点
内存占用大(需存储整个层级节点)
不适合大规模数据或深层结构(如树深度极大)


三.链式二叉树的几种使用

1.计算树的结点个数:

法一:把size作为一个函数的形参,然后把这个树遍历一遍,每遍历一个节点就size(节点个数)加一,但需要注意的是,需要传入size的地址才能改变size的值

void BinaryTreeSize(BTNode* root,int* size)
{if (root == NULL){return;}(*size)++;BinaryTreeSize(root->left,size);BinaryTreeSize(root->right, size);
}

法二:递归,  节点个数=左子树节点个数+右子树节点个数,所以我们以此为基础递归就可以了

int BinaryTreeSize(BTNode* root)
{//节点个数=左子树节点个数+右子树节点个数//递归出口if (root == NULL){return 0;}return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}

2.计算树的叶子结点个数:

叶子结点:即没有左右孩子结点的结点4

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

3.计算树的第k层结点个数:

如上图,当k=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);
}

4.计算树的深度:

int BinaryTreeDeep(BTNode* root)
{//计算树的深度if (root == NULL){return 0;}int lefDep = BinaryTreeDeep(root->left);int rigDep = BinaryTreeDeep(root->right);return 1 + (lefDep > rigDep ? lefDep : rigDep);
}

5.查找结点为X的结点:

BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{//递归出口if (root == NULL){return 0;}if (root->data == x){return root;}//代码走到这里证明根节点并不是我们要找的结点//接下来是左右子树各自分开递归搜查BTNode* left = BinaryTreeFind(root->left, x);if (left)//由于函数最后返回NULL,所以如果这个if条件可以进入就足以说明找到了需要的结点{return root;}BTNode* right = BinaryTreeFind(root->right, x);if (right){return root;}return NULL;
}

6.判断二叉树是否为完全二叉树:

// 判断⼆叉树是否是完全⼆叉树
bool BinaryTreeComplete(BTNode* root)
{Queue q;QueueInit(&q);QueuePush(&q, root);while (!QueueEmpty(&q)){//取队头,出队头BTNode* top = QueueFront(&q);QueuePop(&q);if (top == NULL){break;}//队头结点的左右孩子入队列QueuePush(&q, top->left);QueuePush(&q, top->right);}//队列不为空,继续取队头出队头//1)队头存在非空结点----非完全二叉树//2)队头不存在非空结点----完全二叉树while (!QueueEmpty(&q)){BTNode* top = QueueFront(&q);QueuePop(&q);if (top != NULL){QueueDestroy(&q);return false;}}QueueDestroy(&q);return true;
}

第一个while是为了验证根结点,如果根结点为空,直接返回false,如果根结点不为空,就将树里的结点循环入队列然后继续将子结点入队列并且判断其是否为空,同样的第一次循环的的结束条件是当出现空为止,因此,当来到第二次循环时,树的结点里说明已经第一次循环出了空结点,而当树是完全二叉树时,第二次循环之后队列里应该全是空结点,因此,只要当第二次循环里出现非空结点时,就可以判断其时非完全二叉树(这题思路比较复杂,可以多想一会)

欧克,本次关于链式二叉树的知识点就到此为止了,相关的题目我也会在未来附上

ok,全文终

相关文章:

数据结构:二叉树的链式结构及相关算法详解

目录 一.链式结构的实现 1.二叉树结点基本结构,初始化与销毁: 二.链式结构二叉树的几种遍历算法 1.几种算法的简单区分: 2.前序遍历: 3.中序遍历: 4.后序遍历: 5.层序遍历(广度优先遍历B…...

10.【线性代数】—— 四个基本子空间

十、 四个基本子空间 1. 列空间 C ( A ) C(A) C(A) in R m R^m Rm2. 零空间 N ( A ) N(A) N(A) in R n R^n Rn3. 行空间 C ( A T ) C(A^T) C(AT) in R n R^n Rn4. 左零空间 N ( A T ) N(A^T) N(AT) in R m R^m Rm综述5. 新的向量空间 讨论矩阵 A m ∗ n A_{m*n} Am∗n​…...

计算机黑皮书191本分享pdf

“黑皮书”通常指的是由机械工业出版社出版的计算机科学丛书。这些书籍的封面通常是黑色的,因此得名“黑皮书”。这些书籍涵盖了计算机科学的各个领域,包括操作系统、计算机网络、软件工程、编译原理、数据库等。 获取链接:链接:https://pan…...

MySQL Connector/J下载

MySQL Connector/J下载 下载mysql驱动jar包。 官网:https://downloads.mysql.com/archives/c-j/ 我下载的是8.0.33,下载的时候要注意与MySQL的版本对应。...

AIGC生图产品PM必须知道的Lora训练知识!

hihi,其实以前在方向AIGC生图技术原理和常见应用里面已经多次提到Lora的概念了,但是没有单独拿出来讲过,今天就耐心来一下! 🔥 一口气摸透AIGC文生图产品SD(Stable Diffusion)! 一、…...

【Swift 算法实战】城市天际线问题解法

网罗开发 (小红书、快手、视频号同名) 大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等…...

易错点abc

在同一个输入流上重复创建Scanner实例可能会导致一些问题,包括但不限于输入流的混乱。尤其是在处理标准输入(System.in)时,重复创建Scanner对象通常不是最佳实践,因为这可能导致某些输入数据丢失或者顺序出错。 为什么…...

C++ 正则表达式分组捕获入门指南

在 C 中,正则表达式(regex)是一种用于匹配字符串模式的强大工具。正则表达式不仅能帮助你查找符合特定模式的字符,还能捕获匹配的子字符串(即分组捕获)。这篇文章将介绍 C 正则表达式中的分组捕获机制&…...

AI人工智能机器学习之降维和数据压缩

1、概要 本篇学习AI人工智能机器学习之降维和数据压缩,以主成分分析(PCA, Principal Component Analysis)为例,从代码层面讲述机器学习中的降维和数据压缩。 2、降维和数据压缩 - 简介 在机器学习和数据分析中,降维&…...

17 款电脑压缩工具详解及下载指南(2025 年最新版)

在数字时代,文件压缩是日常工作与生活中不可或缺的操作。无论是视频剪辑师压缩视频以便上传,还是普通用户节省存储空间,一款优质的压缩软件都能极大提升效率。本文将详细介绍 17 款热门电脑压缩软件,涵盖它们的特点、下载地址及适用场景,助你找到最适合自己的工具。 一、…...

DeepSeek开源周Day5压轴登场:3FS与Smallpond,能否终结AI数据瓶颈之争?

2025年2月28日,DeepSeek开源周迎来了第五天,也是本次活动的收官之日。自2月24日启动以来,DeepSeek团队以每天一个开源项目的节奏,陆续向全球开发者展示了他们在人工智能基础设施领域的最新成果。今天,他们发布了Fire-F…...

ROS2软件调用架构和机制解析:Publisher创建

术语 DDS (Data Distribution Service): 用于实时系统的数据分发服务标准,是ROS 2底层通信的基础RMW (ROS Middleware): ROS中间件接口,提供与具体DDS实现无关的抽象APIQoS (Quality of Service): 服务质量策略,控制通信的可靠性、历史记录、…...

【落羽的落羽 C++】C++入门基础·其之一

文章目录 一、C简介1. C的发展历史2. C参考文档 二、namespace命名空间1. C语言的一个缺陷2. namespace3. 命名空间的使用3.1 命名空间成员访问3.2 using展开 一、C简介 1. C的发展历史 C起源于1979年的贝尔实验室,Bjarne Stroustrup(本贾尼博士&#…...

docker使用代理的简单配置

1准备代理服务器 准备代理服务器,例如192.168.120.168:52209 配置docker.service文件 查看service文件的位置 systemctl status docker 编辑service文件 vim /usr/lib/systemd/system/docker.service 添加代理配置 ... [Service] Environment"HTTP_PROXY…...

每日一题-设计食物评分系统,哈希表的有效使用

本题出自LeetCode2353.设计食物评分系统,连着一星期都是设计类的题目哈 题目 设计一个支持下述操作的食物评分系统: 修改 系统中列出的某种食物的评分。返回系统中某一类烹饪方式下评分最高的食物。 实现 FoodRatings 类: FoodRatings(Strin…...

大模型应用:多轮对话(prompt工程)

概述 在与大型语言模型(如ChatGPT)交互的过程中,我们常常体验到与智能助手进行连贯多轮对话的便利性。那么,当我们开启一个新的聊天时,系统是如何管理聊天上下文的呢? 一、初始上下文的建立 1. 创建新会…...

WSDM24-因果推荐|因果去偏的可解释推荐系统

1 动机 可解释推荐系统(ERS)通过提供透明的推荐解释,提高用户信任度和系统的说服力,如下图所示,然而: 1:现有工作主要关注推荐算法的去偏(流行度偏差),但未显…...

VScode在Windows11中配置MSVC

因为MSVC编译器在vs当中,所以我们首先要安装vs的一部分组件。如果只是需要MSVC的话,工作负荷一个都不需要勾选,在单个组件里面搜索MSVC和windows11 SDK,其中一个是编译器,一个是头文件然后右下角安装即可。搜索Develop…...

数据库基础二(数据库安装配置)

打开MySQL官网进行安装包的下载 https://www.mysql.com/ 接着找到适用于windows的版本 下载版本 直接点击下载即可 接下来对应的内容分别是: 1:安装所有 MySQL 数据库需要的产品; 2:仅使用 MySQL 数据库的服务器; 3&a…...

cuda-12.4.0 devel docker 中源码安装 OpenAI triton

1,准备 docker 容器 下载docker image: $ sudo docker pull nvidia/cuda:12.6.2-devel-ubuntu20.04 创建容器: sudo docker run --gpus all -it --name cuda_LHL_01 -v /home/hongleili/ex_triton/tmp1:/root/ex_triton/tmp1 nvidia/cuda:12.6…...

uniapp 对接腾讯云IM群组成员管理(增删改查)

UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...

HBuilderX安装(uni-app和小程序开发)

下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...

2023赣州旅游投资集团

单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

视觉slam十四讲实践部分记录——ch2、ch3

ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

莫兰迪高级灰总结计划简约商务通用PPT模版

莫兰迪高级灰总结计划简约商务通用PPT模版,莫兰迪调色板清新简约工作汇报PPT模版,莫兰迪时尚风极简设计PPT模版,大学生毕业论文答辩PPT模版,莫兰迪配色总结计划简约商务通用PPT模版,莫兰迪商务汇报PPT模版,…...

【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制

使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...