【数据结构】链式二叉树详解

在这里插入图片描述
个人主页~
链式二叉树基本内容~


链式二叉树详解

  • 1、通过前序遍历的数组来构建二叉树
  • 2、二叉树的销毁
  • 3、二叉树节点个数
  • 4、二叉树叶子节点个数
  • 5、二叉树第k层节点个数
  • 6、二叉树查找
  • 7、前序遍历
  • 8、中序遍历
  • 9、后序遍历
  • 10、层序遍历与检查二叉树是否为完全二叉树
    • Queue.h
    • Queue.c
    • 层序遍历代码
    • 完全二叉树判断

整个链式二叉树以递归定义为主,需要详细了解递归的相关概念:递归定义在第六条
最需要记住的是:递归定义中的return是退出到上一级,而不是整个程序

1、通过前序遍历的数组来构建二叉树

BTNode* BinaryTreeCreate(BTDataType* a,int n, int* pi)
{if (*pi >= n || a[*pi] == '#'){ // 如果到达数组末尾或遇到#,则返回NULL  (*pi)++;//移动到下一个数据return NULL;}BTNode* node = BuyNode(a[*pi]);(*pi)++; // 移动到下一个数据node->left = BinaryTreeCreate(a, n, pi); // 递归创建左子树  node->right = BinaryTreeCreate(a, n, pi); // 递归创建右子树  return node;
}

建树过程(部分过程省略):
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2、二叉树的销毁

二叉树销毁是不能够从第一层开始销毁的,这样我们不能销毁所有的节点,从叶节点开始销毁,递归释放,才能销毁二叉树所有节点

void BinaryTreeDestory(BTNode* root)
{if (root == NULL)return;BinaryTreeDestory(root->left);//找到底层左节点BinaryTreeDestory(root->right);//找完左节点找右节点free(root);
}

在这里插入图片描述
找到D的左子结点,是#返回,再找D的右节点,是#返回,然后释放掉D节点,此时B的root->left结束,进行root->right,以此类推,这样会从最底下的叶节点开始将所有节点释放

3、二叉树节点个数

int BinaryTreeSize(BTNode* root)
{//return root == NULL ? 0 : BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;if (root == NULL)return 0;return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}

两种表达方式,一种是普通表达,另一种是三目表达
如果当前节点为空,返回0,如果左右子节点都遍历完了,将结果+1返回
在这里插入图片描述
递归走到D的左子结点,返回到D,return 0
右子节点,返回到D,return 0
函数走完返回到B,return 0+0+1
以此类推

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

当前节点为空时,返回0
当前节点不为空且左右子节点都为空时,说明该节点为叶节点,返回1
将左子树的叶节点与右子树的叶节点相加就是二叉树总共的叶子结点个数
在这里插入图片描述
A走到B,B走到D,D的左右节点都为空,D是叶子结点,返回1,返回到B
再走E的左子结点,为空,返回0,走E节点,E节点的左右子节点为空,为叶子节点返回1,以此类推

5、二叉树第k层节点个数

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

当节点为0时,返回0
当k为1时,只有根节点,返回1
每次递归会使k减1,到第k层时k=1,然后就开始返回,这样递归的定义可以保证第k层的所有个数都可以算到
在这里插入图片描述
当我们想要求第三层的节点个数时,我们找到BC两棵子树,此时对于BC来说,它们需要找到它第二层的节点个数,再向下递归,此时k==1,将它们不为空的节点返回1

6、二叉树查找

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

当节点为空时,返回空
当节点数据为想要查找的数据时,返回该节点指针
递归调用,当左子树中存在这个数时,ret1不为空,返回的就是那个值,右子树同上,都没有就返回空
在这里插入图片描述

7、前序遍历

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

前序遍历的顺序:根节点->左子树->右子树
在这里插入图片描述
先将根节点A打印之后,递归到左子结点B,打印B,递归到B的左子结点D,打印D,D的左子节点为空,打印N,查看右子节点,也为空,打印N,返回到B,查看右子结点,打印E,以此类推

8、中序遍历

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

中序遍历顺序:左子树->根->右子树
在这里插入图片描述
A到B,B到D,D到最底的左子节点,为空,打印N,再打印根D,右子节点,为空,打印N,然后回到B看E,以此类推

9、后序遍历

void BinaryTreePostOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}BinaryTreeInOrder(root->left);BinaryTreeInOrder(root->right);printf("%c ", root->data);
}

后序遍历顺序:左子树->右子树->根
在这里插入图片描述
A到B,B到D,D到最底的左子节点,为空,打印N,看D的右子节点,为空,打印N,最后打印D
去到B的右子节点E,以此类推

10、层序遍历与检查二叉树是否为完全二叉树

层序遍历即一层一层的遍历,从第一层开始,此时我们需要一个队列,因为队列可以实现先入先出,并且可以存储数据

Queue.h

#pragma once#include <stdio.h>
#include <stdlib.h>
#include <assert.h>typedef struct BinaryTreeNode* QDataType;// 链式结构:表示队列
typedef struct QListNode
{struct QListNode* pNext;QDataType data;
}QNode;
// 队列的结构
typedef struct Queue
{QNode* front;QNode* rear;int size;
}Queue;
// 初始化队列
void QueueInit(Queue* q);
// 队尾入队列
void QueuePush(Queue* q, QDataType node);
// 队头出队列
void QueuePop(Queue* q);
// 获取队列头部元素
QDataType QueueFront(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q);
// 销毁队列
void QueueDestroy(Queue* q);

Queue.c

队列我就不添加注释了,前边的文章-栈和队列中都有,可以自行翻阅

#include "Queue.h"void QueueInit(Queue* q)
{assert(q);q->front = q->rear = NULL;q->size = 0;
}void QueuePush(Queue* pq, QDataType x)
{assert(pq);QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("malloc fail\n");return;}newnode->data = x;newnode->pNext = NULL;if (pq->rear == NULL){assert(pq->front == NULL);pq->front = pq->rear = newnode;}else{pq->rear->pNext = newnode;pq->rear = newnode;}pq->size++;
}void QueuePop(Queue* q)
{assert(q);assert(!QueueEmpty(q));if (q->front->pNext == NULL){free(q->front);q->front = q->rear = NULL;}else{QNode* next = q->front->pNext;free(q->front);q->front = next;}q->size--;
}QDataType QueueFront(Queue* q)
{assert(q);assert(!QueueEmpty(q));if (q->front == NULL){return NULL;}return q->front->data;
}int QueueEmpty(Queue* q)
{assert(q);return q->size == 0;
}void QueueDestroy(Queue* q)
{assert(q);QNode* pur = q->front;while (pur){QNode* next = pur->pNext;free(pur);pur = next;}q->front = q->rear = NULL;q->size = 0;
}

层序遍历代码

void BinaryTreeLevelOrder(BTNode* root)
{Queue q;QueueInit(&q);if (root)QueuePush(&q, root);//把根节点作为队列的队头while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);//将要出队的队头数据存储一下QueuePop(&q);//将队头弹出printf("%c ", front->data);//打印被存储的队头数据if (front->left)QueuePush(&q, front->left);if (front->right)QueuePush(&q, front->right);}//从队头开始检查左右子节点,若不为空则添加入队printf("\n");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){QueueDestroy(&q);return 0;}}QueueDestroy(&q);return 1;
}

在这里插入图片描述


今日分享完毕,瑞思拜~

在这里插入图片描述

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

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

相关文章

WordPress子比内容同步插件

1.支持分类替换 将主站同步过来的文章分类进行替换 2.支持本地化文章图片 &#xff08;使用储存桶可能会导致无法保存图片&#xff09; 3.支持自定义文章作者&#xff08;选择多个作者则同步到的文章作者将会随机分配&#xff09; 4.支持将同步过来的文章自定义文章状态&…

Java | Leetcode Java题解之第128题最长连续序列

题目&#xff1a; 题解&#xff1a; class Solution {public int longestConsecutive(int[] nums) {Set<Integer> num_set new HashSet<Integer>();for (int num : nums) {num_set.add(num);}int longestStreak 0;for (int num : num_set) {if (!num_set.contai…

乡村振兴与文化传承:挖掘乡村历史文化资源,传承乡村优秀传统,打造具有地方特色的美丽乡村文化品牌

目录 一、引言 二、乡村历史文化资源的挖掘与保护 &#xff08;一&#xff09;乡村历史文化资源的内涵 &#xff08;二&#xff09;乡村历史文化资源的挖掘 &#xff08;三&#xff09;乡村历史文化资源的保护 三、乡村优秀传统的传承与创新 &#xff08;一&#xff09;…

4.基础纹理

纹理的目的&#xff1a;使用一张图片来控制模型的外观纹理映射技术&#xff1a;把一张图“黏”在模型表面&#xff0c;逐纹素&#xff08;与像素不同&#xff09;地控制模型颜色通常在建模软件中利用纹理展开技术实现&#xff0c;把纹理映射坐标存储在每个顶点上纹理映射坐标&a…

php--无回显情况下的命令执行

免责声明:本文仅做技术交流与学习... 目录 绕过模版: 1. 写入文件、二次返回: 2. DNS信道: 3.http信道: 4.反弹shell信道 -->公网IP 5.延时 sleep 3 建立通道(信道) --数据传输的路径 shell_exec 与 system 相比&#xff0c;shell_exec没有回显结果. 绕过模版:…

Java中的Instant

在Java中&#xff0c;Instant 是 java.time 包中的一个类&#xff0c;用于表示时间轴上的一个瞬时点&#xff0c;通常以纳秒精度表示。它通常用于表示机器可读的时间戳&#xff0c;而不是人类可读的时间表示&#xff08;如日期和时间&#xff09;。 Instant 主要用于时间计算和…

数学基础——微积分在机器/深度学习上的应用

目录 微分学 导数 偏导数 梯度 梯度下降算法 反向传播算法 自动求导 计算图 正则化与过拟合 L1正则化 L2正则化 Dropout正则化 拉格朗日对偶问题 拉格朗日乘数法 凸优化 对偶问题 KKT条件 Slater条件 积分学 笔记内容 微积分是17世纪后半叶发展起来的数…

【Leetcode每日一题】 动态规划 - 简单多状态 dp 问题 - 买卖股票的最佳时机含冷冻期(难度⭐⭐)(79)

1. 题目解析 题目链接&#xff1a;309. 买卖股票的最佳时机含冷冻期 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 二、算法思路 1. 状态表示 dp[i][0]&#xff1a;表示第 i 天结束后&#xff0c;处于「买入」状态…

【计算机毕业设计】331基于微信小程序的家庭财务管理系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

freebsd 14.0 golang qt 修改源码并正确的安装方法

看了网的方法&#xff0c;包括官方的&#xff0c;没有一种方法能安装成功&#xff0c;这是我通过摸索安装成功的方法&#xff1a; 1. 安装好golang qt5:pkg install liteide qt5 2.qt go源码enn.go修改源码为&#xff1a; path : filepath.Join(QT_DIR(), "" /*QT…

音视频开发15 FFmpeg FLV封装格式分析

FLV(Flash Video)简介 FLV(Flash Video)是Adobe公司推出的⼀种流媒体格式&#xff0c;由于其封装后的⾳视频⽂件体积⼩、封装简单等特点&#xff0c;⾮常适合于互联⽹上使⽤。⽬前主流的视频⽹站基本都⽀持FLV。采⽤ FLV格式封装的⽂件后缀为.flv。 FLV封装格式的组成 FLV封装…

AI学习指南机器学习篇-逻辑回归算法简介

AI学习指南机器学习篇-逻辑回归算法简介 在机器学习领域&#xff0c;逻辑回归是一种常用的分类算法&#xff0c;它可以用于预测一个事件发生的概率。逻辑回归不同于线性回归&#xff0c;它的输出是一个概率&#xff0c;而不是一个连续的数值。本文将介绍逻辑回归的基本概念&am…

屏幕适配方案

百分比布局 高度固定&#xff0c;宽度使用百分比&#xff0c;通常使用max-width/min-width控制尺寸范围过大或者过小。但是屏幕尺寸相对于设计稿跨度过大的话&#xff0c;会拉伸明显。常用在pc端适配。 flex布局 屏幕尺寸相对于设计稿跨度过大的话&#xff0c;会拉伸明显。常用…

【linux软件基础知识】执行进程切换和switch_to

执行进程切换 进程切换可能只发生在精心定义的点: schedule()函数。这里,我们仅关注内核如何执行一个进程切换,从本质上说,每个进程切换由两步组成, 切换页全局目录以安装一个新的地址空间,切换内核态堆栈和硬件上下文,因为硬件上下文提供了内核执行新进程所需要的所有信息…

牛客BM22 比较版本号【中等 字符串 Java/Go/PHP/C++】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/2b317e02f14247a49ffdbdba315459e7 思路 字符串用.分割为数组&#xff0c;然后依次比较大小Java代码 import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修…

Linux常用命令整理

一、文件和目录操作 1. ls 列出目录内容选项&#xff1a;-l&#xff08;详细列表&#xff09;、-a&#xff08;包括隐藏文件&#xff09;、-h&#xff08;以人类可读的格式显示文件大小&#xff09; 2. cd 改变当前目录用法&#xff1a;cd 目录名 3. pwd 显示当前工作目录…

lvs安装文档

目录 1. 检测内核是否支持了lvs 2. 安装依赖 3. 安装ipvsadm 1. 检测内核是否支持了lvs Centos6.5的内核已经支持了lvs&#xff0c;所有只需要安装lvs的管理工具ipvsadm即可&#xff1b; 检查linux内核是否集成lvs模块&#xff1a;modprobe -l | grep ipvs 开启路由转发功…

欧洲版“OpenAI”——Mistral 举办的 AI 大模型马拉松

近期&#xff0c;法国的 Mistral AI 举办了一场别开生面的 AI 大模型马拉松。要知道&#xff0c;Mistral 可是法国对 OpenAI 的有力回应&#xff0c;而且其技术还是完全开源的呢&#xff01;这场在巴黎举行的黑客马拉松&#xff0c;规模空前盛大&#xff0c;竟然有超过 1000 名…

提升你的Netty服务器性能:零拷贝与Reactor模型精讲

1.深入Netty的核心原理 1.1 Netty架构概览 Netty是目前使用广泛的Java NIO客户端服务器框架&#xff0c;它能够快速构建可维护的高性能协议服务器与客户端。起初&#xff0c;它由JBoss提供&#xff0c;现完全成为社区驱动的项目。Netty提供了一种异步的、事件驱动的网络应用程…

二进制分析 ELF格式

简介: ELF 用于可执行文件&#xff0c;对象文件&#xff0c;共享库及核心转储&#xff0c;是UN IX系统实验室作为二进制接口而开发和发布 组成部分ELF 头部 该头部是一系列结构化的字节&#xff0c;描述是什么样的二进制文件及在文件的什么地方找到其他内容 可以在 /usr/includ…