数据结构初阶-二叉树链式
目录
1.概念与结构
2.二叉数链式的实现
2.1遍历规则
2.2申请内存空间
2.3手动构建一棵二叉树
2.4二叉树结点的个数
2.5二叉树叶子结点的个数
2.6二叉树第K层结点个数
2.7二叉树的高度
2.8二叉树中查找值为x的结点
2.9二叉树的销毁
3.层序遍历
3.1概念
3.2层序遍历的实现
4.判断是否为完全二叉树
5.总结
1.概念与结构
用链表来表示一棵二叉树,即用链表来指示元素的逻辑关系。通常的方法是链表中每一个由三个域组成:数据域,左指针域和右指针域。左右指针分别用来给出该结点的左孩子和右孩子所在的链结点的存储地址,最终结点的结构是下面所示:
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType data;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;
由于根结点的左子树分别又是由子树结点、子树结点的左子树、子树结点的右子树组成的。因此二叉树定义是递归式的,链式二叉树的操作中基本都是按照该概念实现的。所以我们在二叉树的各种操作中都要用到递归操作,所以代码比较简单,但是比较难写出来!
2.二叉数链式的实现
2.1遍历规则
(1)前序遍历
访问根结点的操作发生在左右结点之前,即我们先遍历更结点然后再是左结点,最后才是右结点。

我们从A开始进入二叉树,先打印A结点的数据,再打印A结点的左孩子即为B,再同理推出D,由于D的左孩子为NULL,打印完NULL后再进入D的右孩子,又为NULL,然后返回到B ,B 的左孩子已经打印完了,所以我们开始打印B的右孩子,由于为NULL,所以我们返回到B,B 子树已经全部遍历完了,所以我们开始遍历C 子树,以此类推,最终打印出顺序为:ABDNULLNULLNULLCENULLNULLFNULLNULL这就是我们所说的前序遍历。
代码实现:
//前序遍历
void PreOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}printf("%c", root->data);//我们主要是打印出这个结点的位置,所以我们用字母来表示这个//我们要把开始typedef的int 改为charPreOrder(root->left);PreOrder(root->right);
}
(2)中序遍历
中序遍历是先从左结点开始遍历后面是根结点最后是右结点。容易知道上副图的遍历结果为ABDNULLNULLNULLCENULLNULLFNULLNULL,所以最终代码实现是:
//中序遍历
void InOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}InOrder(root->left);printf("%c ", root->data);InOrder(root->right);
}
(3)后序遍历
后序遍历是先去遍历左结点,再是右结点最后才是根结点,所以开始的运行结果为NULL NULL D NULL B NULL NULL E NULL NULL F C A,代码实现:
//后序遍历
void PostOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}PostOrder(root->left);PostOrder(root->right);printf("%c ", root->data);
}
2.2申请内存空间
我们需要先malloc一个结点大小的空间并且把传入的数据赋值给data,左孩子和右孩子指针置为NULL,所以代码如下:
//申请内存空间
BTNode* buyNode(BTDataType x)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));node->data = x;node->left = node->right = NULL;
}
2.3手动构建一棵二叉树
我们如果和我们之前的那张图片一样构造二叉树的结果一样的话,我们可以写出以下代码:
//手动构造一块二叉树
BTNode* CreateTree()
{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->left = nodeE;nodeC->right = nodeF;return nodeA;
}
2.4二叉树结点的个数
我们发现二叉树结点个数等于左子树的结点个数加上右结点个数加上根结点的个数(1)构成,而左子树也等于左二叉树的结点个数加上右二叉树的结点个数加上根结点的个数(1)构成,所以最终代码如下:
//二叉树结点个数
int BinaryTreeSize(BTNode* root)
{if (root == NULL){return 0;}return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}
2.5二叉树叶子结点的个数
我们发现二叉树叶子结点的个数=左子树叶子结点的个数+右子树叶子结点的个数,所以最终代码如下:
//叶子结点的个数
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);
}
2.6二叉树第K层结点个数
我们发现二叉树第K层结点个数=左子树第K层结点的个数+右子树第K层结点的个数,所以最终代码如下:
//二叉树第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);
}
2.7二叉树的高度
我们要比较的是左子树深度和右子树的深度,比较大的一方才算,即为根结点+max(左子树,右子树)我们先调用一下左子树的递归序列,并把左子树的深度存储起来,然后把右子树按照同样的方法进行存储,后面返回其中最大的一个加1即可,最终代码如下:
//求二叉树的深度
int BinaryTreeDepth(BTNode* root)
{if (root == NULL){return 0;}int leftDep = BinaryTreeDepth(root->left);int rightDep = BinaryTreeDepth(root->right);return 1 + (leftDep > rightDep ? leftDep : rightDep);
}
2.8二叉树中查找值为x的结点
我们用前序遍历方法进行查找,代码如下:
//二叉树中查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL){return NULL;}if (root->data == x){return root;}BTNode* leftFind = BinaryTreeFind(root->left, x);if (leftFind){return leftFind;}BTNode* rightFind = BinaryTreeFind(root->right, x);if (rightFind){return rightFind;}return NULL;
}
2.9二叉树的销毁
我们需要传一个指针,然后先进行左子树的销毁,最后再进行右子树的销毁,所以代码如下:
//二叉树的销毁
void BinaryTreeDestory(BTNode** root)
{if (*root == NULL){return;}BinaryTreeDestory(&(*root)->left);//->的优先级大于*BinaryTreeDestory(&(*root)->right);free(*root);*root = NULL;
}
3.层序遍历
3.1概念
层序遍历就是从二叉树的根结点出发,依次往左孩子到右孩子进行访问,像我们上一副图的层序遍历就是ABCDEF。
3.2层序遍历的实现
先说结论,我们用队列来实现层序遍历,先进行根结点入队列,若发现不为空,则把队头数据进行出队操作,若取出的结点有左孩子(右孩子),则把左孩子(右孩子)入队列,依次类推。所以我们需要队列这个数据结构的帮助。我们把之前的代码复制过来有
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include<stdbool.h>
//定义队列结点的结构
typedef int STDataType;
typedef struct QueueNode
{STDataType data;struct QueueNode* next;
}QueueNode;
//队列的结构
typedef struct Queue
{QueueNode* phead;QueueNode* ptail;
}Queue;
//队列的初始化
void QueueInit(Queue* pq)
{assert(pq);//防止传参为空pq->phead = pq->ptail = NULL;
}
//入队列
void QueuePush(Queue* pq, STDataType x)
{assert(pq);QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));if (newnode == NULL){perror("malloc fail!\n");exit(1);}newnode->data = x;newnode->next = NULL;if (pq->phead == NULL){//也可以写为//pq->phead==pq->ptailpq->phead = pq->ptail = newnode;}else{pq->ptail->next = newnode;pq->ptail = newnode;//也可以把pq->ptail=newnode写到if-else语句之后}
}
//队列判空
bool QueueEmpty(Queue* pq)
{assert(pq);return pq->phead == NULL;
}
//队列有效元素的个数
int QueueSize(Queue* pq)
{int size = 0;QueueNode* pcur = pq->phead;while (pcur){size++;pcur = pcur->next;}return size;
}
//出队列
void QueuePop(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));if (pq->phead == pq->ptail){free(pq->phead);pq->phead = pq->ptail = NULL;}else{QueueNode* next = pq->phead->next;free(pq->phead);pq->phead = next;}
}
//取队头数据
STDataType QueueFront(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->phead->data;
}
//取队尾数据
STDataType QueueBack(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->ptail->data;
}
//销毁队列
void QueueDestroy(Queue* pq)
{assert(pq);QueueNode* pcur = pq->phead;while (pcur){QueueNode* next = pcur->next;free(pcur);pcur = next;}pq->phead = pq->ptail = NULL;//可以加pq->size=0
}
//测试函数
void test()
{Queue q;QueueInit(&q);QueuePush(&q, 1);QueuePush(&q, 2);QueuePush(&q, 3);QueuePush(&q, 4);int size = QueueSize(&q);printf("%d\n", size);QueuePop(&q);size = QueueSize(&q);printf("%d\n", size);size = QueueFront(&q);printf("%d\n", size);size = QueueBack(&q);printf("%d\n", size);QueueDestroy(&q);
}
int main()
{test();return 0;
}
我们需要改以下的东西:typedef struct BiaryTreeNode* STDataType;(我们所存储的数据类型是struct BinaryTreeNode*),之所以是指针,因为我们之后要使用二级指针,这样子更方便,所以最终实现有:
//层序遍历
void LevelOrder(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);}}QueueDestroy(&q);
}
4.判断是否为完全二叉树
完全二叉树有两个特点:除最后一层外其余层数的结点个数都达到最大;最后一层结点从左到右依次排列。代码实现如下(我会注释):
//判断是否为完全二叉树
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);}//队列不一定为空,继续取队头出队头while (!QueueEmpty(&q)){BTNode* top = QueueFront(&q);QueuePop(&q);if (top != NULL){QueueDestroy(&q);return false;}}QueueDestroy(&q);return true;
}
5.总结
二叉树链式代码比较简单,但是实现这个比较难。下节我将讲解二叉树的应用,喜欢的可以一键三连哦。

相关文章:
数据结构初阶-二叉树链式
目录 1.概念与结构 2.二叉数链式的实现 2.1遍历规则 2.2申请内存空间 2.3手动构建一棵二叉树 2.4二叉树结点的个数 2.5二叉树叶子结点的个数 2.6二叉树第K层结点个数 2.7二叉树的高度 2.8二叉树中查找值为x的结点 2.9二叉树的销毁 3.层序遍历 3.1概念 3.2层序遍历…...
Springboot 集成 Flowable 6.8.0
1. 创建 Spring Boot 项目 通过 Spring Initializr(https://start.spring.io/ )创建一个基础的 Spring Boot 项目,添加以下依赖: Spring WebSpring Data JPAMySQL DriverLombok(可选,用于简化代码&#x…...
协作机械臂需要加安全墙吗? 安全墙 光栅 干涉区
安全墙是什么 文章目录 安全墙是什么简介1. 物理安全墙1.1 定义:1.2 作用机制:1.3 应用场景: 2. 虚拟安全墙2.2 定义:2.3 作用机制:2.3 应用场景: 3. 安全毛毯3.1 工作原理:3.2 特点3.3 应用场景…...
HTML5 SVG:图形绘制的现代标准
HTML5 SVG:图形绘制的现代标准 引言 随着互联网技术的发展,网页的交互性和美观性日益受到重视。HTML5 SVG作为一种强大的图形绘制技术,在网页设计中发挥着重要作用。本文将深入探讨HTML5 SVG的原理、应用场景以及如何在实际项目中运用。 一、HTML5 SVG简介 1.1 什么是SV…...
洛谷题单1-B2025 输出字符菱形-python-流程图重构
题目描述 用 * 构造一个对角线长 5 5 5 个字符,倾斜放置的菱形。 输入格式 没有输入要求。 输出格式 如样例所示。用 * 构成的菱形。 输入输出样例 #1 输入 #1 输出 #1 **** *********方式-前半区推导,后半区逆序 代码 class Solution:static…...
springboot+mybatisplus
1.什么是springboot? Spring Boot是一个用于快速构建Spring应用程序的框架。它旨在帮助开发人员快速搭建Spring框架,减少配置和繁琐的工作。Spring Boot继承了原有Spring框架的优秀基因,使Spring在使用中更加方便快捷。 在Spring Boot中集成ActiveMQ,需要导入相应的starter…...
《TypeScript 面试八股:高频考点与核心知识点详解》
“你好啊!能把那天没唱的歌再唱给我听吗? ” 前言 因为主包还是主要学习js,ts浅浅的学习了一下,在简历中我也只会写了解,所以我写一些比较基础的八股,如果是想要更深入的八股的话还是建议找别人的。 Ts基…...
Golang os模块功能详解与示例
os 是 Go 语言标准库中与操作系统交互的核心模块,提供了丰富的功能来操作文件系统、进程、环境变量等。下面我将详细介绍 os 模块的主要功能,并提供相应的代码示例。 1. 文件与目录操作 1.1 文件操作 创建文件 package mainimport ("fmt"&…...
SICAR 标准 KUKA 机器人标准功能块说明手册
功能块名称:LSicar_Robot_KUKA_PrD 目录 1. 概述 2. 功能说明 2.1 程序控制 2.2 状态监控 2.3 报警与故障处理 2.4 驱动控制 3. 关键参数说明 4. 操作步骤指南 4.1 初始化配置 4.2 运行控制 4.3 状态监控 5. 常见故障处理 6. 注意事项 附录1:程序段索引 附录…...
linux中如何修改文件的权限和拥有者所属组
目录标题 chmod指令八进制形式权限修改文件拥有者所属组的修改umask有关内容 chmod指令 chmod指令可以用来修改人员的权限其形式如下: u代表的是拥有者,g代表的是所属组,o代表的是其他人,a表示所有人,如果你想增加权…...
掌握Linux项目自动化构建:从零入门make与Makefile
文章目录 前言: 一、初识自动化构建工具1.1 什么是make/Makefile?1.2 快速体验 二、深入理解核心机制2.1 依赖关系与依赖方法2.2 伪目标的妙用2.3 具体语法a.makefile的基本雏形b.makefile推导原则! 三、更加具有通用型的makefile1. 变量定义…...
Jenkins 配置python项目和allure
Jenkins新建项目 新建ry-api-auto-test。 添加项目描述,选择gitee令牌。 源码管理,设置仓库地址和凭证。参考我上一篇文章的链接:配置gitee私人令牌和凭证 构建步骤,因为我Jenkins部署在Windows,因此选择batch。…...
优化 Docker 镜像 技巧
优化 Docker 镜像可以提高构建速度、减少镜像大小、提高安全性和效率。以下是一些优化 Docker 镜像的方法: 使用适当的基础镜像 选择合适的基础镜像可以减小镜像大小,并确保基础镜像的安全性和更新性。Alpine、Ubuntu Minimal 等轻量级基础镜像是常用选…...
从简单场景认识建造者模式
建造者设计模式总的来说常见的形式无非就两种。 一种是具体产物样式多,故通过中间者(指挥者)来统筹决定产生哪种对象(组装电脑,都是电脑,只是参数配置不同)。 一种是构造的可选参数多…...
Maven工具学习使用(四)——仓库
仓库分类 对于Mavne来说,仓库只分为两类:本地仓库和远程仓库。当Maven根据坐标查询寻找构件的时候,它首先会查看本地仓库,如果本地仓库存在此构件,则直接使用;如果本地仓库不存在此构件,或者需要查看是否有更新的构件版本,Maven就会去远程仓库查找,发现需要的构件之后…...
vue3:十一、主页面布局(进入指定菜单页面,默认锁定到左侧菜单)
一、效果 直接进入home页面,直接展开对应的菜单项 二、具体实现 1、菜单容器增加默认选中变量 在菜单容器中将默认展开菜单default-openeds修改为默认选中菜单default-active 2、引入useRoute方法 引入该方法为了获取当前页面的路径 import { useRoute } from …...
linux,防火墙,firewall,常用命令
文章目录 1. 查看防火墙状态2. 查看当前开放的端口和服务查看所有开放的端口查看所有允许的服务查看所有区域的详细信息 3. 开放指定端口开放端口(临时生效)开放端口(永久生效)开放指定端口范围 4. 删除指定端口删除端口ÿ…...
SQL 函数
SQL 函数 概述 SQL 函数是数据库查询语言(Structured Query Language)的核心组成部分之一。它们是用于执行特定任务的预定义过程,可以在查询中使用以增强查询的灵活性和功能性。SQL 函数可以分为两大类:内置函数和用户自定义函数…...
【蓝桥杯】每日练习 Day13
前言 今天做了不少题,但是感觉都太水了,深思熟虑之下主播决定拿出两道相对不那么水的题来说一下(其实还是很水)。 两道问题,一道是日期问题(模拟),一道是区间合并问题。 日期差值 …...
【Docker系列七】Docker Compose 命令详解
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...
【AI学习】Transformer 模型
1,概念 是一种基于自注意力机制(Self-Attention Mechanism)的深度学习架构,在自然语言处理、计算机视觉等多个领域都有着极为重要的应用。 2,基本结构 1)编码器(Encoder) 通常由多个相同的编码器层堆叠而成。 每个编码器层包含了多头自注意力机制、前馈神经网络以及…...
大数据学习栈记——HBase操作(shell java)
本文介绍HBase在shell终端的常见操作以及如何利用java api操作HBase,操作系统:Ubuntu24.04 参考: https://blog.51cto.com/u_16099228/8016429 https://blog.csdn.net/m0_37739193/article/details/73618899 https://cloud.tencent.com/d…...
React多层级对象改变值--immer
reduxjs/toolkit底层就是immer,,,所以在使用redux的时候,直接赋值,就会响应式的数据 如果不使用reduxjs/toolkit,可以自己使用immer来实现 安装immer npm install immer引入produce函数,,prod…...
服务器硬盘爆满100%问题解决
问题 在工作中遇到一个服务器,服务器硬盘100%,查找哪个目录文件中占用大量空间。发现加起来才150G,硬盘空间大概有500G。 处理问题,排查是否有某个进程正在删除文件,进程卡住了,所以过滤一下有哪些进程&am…...
智能制造:物联网和自动化之间的关系
工业自动化 工业自动化是机器设备或生产过程在不需要人工直接干预的情况下按预期的目标实现测量、操纵等信息处理和过程控制的统称。 在传统的工业生产过程中,很多环节需要人工操作,比如设备调试、生产监控、质量检测等。然而,随着工业自动化…...
Axure项目实战:智慧城市APP(三)教育查询(显示与隐藏交互)
亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢! 课程主题:教育查询 主要内容:教育公告信息,小升初、初升高、高考成绩查询;教育公告信息为传统的信息页面,小升…...
01 设计模式和设计原则
类设计原则: 单一职责原则(Single Responsibility Principle,SRP):实现类要职责单一开闭原则(Open Close Principle,OCP):对扩展开放,对修改关闭里氏替换原则…...
Github 2025-03-23 php开源项目日报Top10
根据Github Trendings的统计,今日(2025-03-23统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量PHP项目10TypeScript项目1JavaScript项目1Shell项目1Laravel: 以优雅语法简化Web开发 创建周期:4028 天开发语言:PHP协议类型:MIT LicenseSt…...
macbook电脑如何清理键盘防止误触
M1芯片的MacBook电脑关机后按任意键开机,是苹果的功能设计。这样设计的目的是为了方便用户,让用户在想要使用电脑时能快速开机。但是清理电脑键盘的时候却成为了一种苦恼 以下是一些清理 MacBook 键盘防止误触的方法: 使用工具锁定键盘 Cle…...
AIMB-ASMB-788B(PPC-MB-620B)RAID驱动安装(笔记版)
创建RAID后安装系统时看不到磁盘信息,以下案例是安装windows10系统时如何安装主板RAID驱动,由于是笔记版不做过多介绍。 RAID驱动链接:https://advdownload.advantech.com.cn/productfile/Downloadfile1/1-2MAHDQD/AIMB-788_788E_RAID_AHCI_…...
