数据结构初阶-二叉树链式

目录

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.总结

二叉树链式代码比较简单,但是实现这个比较难。下节我将讲解二叉树的应用,喜欢的可以一键三连哦。

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

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

相关文章

SpringMVC拦截器

SpringMVC拦截器 什么是拦截器拦截器和过滤器的区别SpringMVC 拦截器的工作原理拦截器的配置拦截器的配置主要有两种方式XML 配置方式Java配置方式 创建一个拦截器拦截器的应用场景拦截器的执行顺序 什么是拦截器 SpringMVC 的拦截器&#xff08;Interceptor&#xff09;是指在…

cocos creator 笔记-路边花草

版本&#xff1a;3.8.5 实现目标&#xff1a;给3d道路生成路边景观花草 在场景下创建一个节点&#xff0c;我这里种植两种花草模型&#xff0c;兰花和菊花&#xff0c;所以分别在节点下另创建两个节点&#xff0c;为了静态合批。 1.将花草模型分别拖入场景中&#xff0c;制作…

YoloV8训练和平精英人物检测模型

概述 和平精英人物检测&#xff0c;可以识别游戏中所有人物角色&#xff0c;并通过绘制框将人物选中&#xff0c;训练的模型仅仅具有识别功能&#xff0c;可以识别游戏中的视频、图片等文件&#xff0c;搭配Autox.js可以推理&#xff0c;实现实时绘制&#xff0c;但是对手机性…

达梦改密码时不想看到明文

有时&#xff0c;需要修改用户密码 但不想让别人看到你的密码明文 最简单的是用manager工具 找到用户&#xff0c;点击右键&#xff0c;选择modify 这里很明显被掩盖为黑点了&#xff0c;放心输入即可 想通过命令行怎么办&#xff1f; 这不&#xff0c;密码全被看到了 用变…

3. 轴指令(omron 机器自动化控制器)——>MC_GearInPos

机器自动化控制器——第三章 轴指令 17 MC_GearInPos变量▶输入变量▶输出变量▶输入输出变量 功能说明▶时序图▶重启运动指令▶多重启动运动指令▶异常 示例程序▶动作示例▶梯形图▶结构文本(ST) MC_GearInPos 设定主轴和从轴间的齿轮比&#xff0c;进行电子齿轮动作。 指定…

【教学类-58-14】黑白三角拼图12——单页1页图。参考图1页6张(黑白、彩色)、板式(无圆点、黑圆点、白圆点)、宫格2-10、张数6张,适合集体操作)

背景需求&#xff1a; 基于以下两个代码&#xff0c;设计一个单页1页黑白三角、彩色三角&#xff08;包含黑点、白点、无点&#xff09;的代码。 【教学类-58-12】黑白三角拼图10&#xff08;N张参考图1张操作卡多张彩色白块&#xff0c;适合个别化&#xff09;-CSDN博客文章…

pytest-xdist 进行高效并行自动化测试

pytest-xdist 的核心功能是通过多进程分发测试任务&#xff0c;每个进程独立运行测试&#xff0c;确保测试隔离。2025 年 3 月 25 日&#xff0c;pytest-xdist 在 GitHub 上已有超过 1,200,000 次下载&#xff0c;表明其在测试社区中的广泛接受。 在自动化测试中&#xff0c;随…

18502 字符串哈希匹配字符串

18502 字符串哈希匹配字符串 ⭐️难度&#xff1a;中等 &#x1f31f;考点&#xff1a;字符串hash &#x1f4d6; &#x1f4da; import java.util.Arrays; import java.util.LinkedList; import java.util.Queue; import java.util.Scanner;public class Main {static int…

通过git文件查看大模型下载链接的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

Linux设备永久挂载

一、fstab文件详解 下面是我虚拟机中的一个fstab文件,可以看到有四行配置,每一行有6个字段,下面我会对字段的含义进行讲解。 /etc/fstab 文件包含了如下字段,通过空格或Tab分隔: file systems:要挂载的分区或存储设备;dir:挂载位置;type:要挂载设备或分区的文件系统…

Linux系统初始化脚本

Rocky、Almalinux、CentOS、Ubuntu、Debian、openEuler、AnolisOS、OpenCloudOS、openSUSE、银河麒麟&#xff08;Kylin Server&#xff09;和统信&#xff08;Uos Server&#xff09;系统初始化脚本 Shell脚本源码地址&#xff1a; Gitee&#xff1a;https://gitee.com/raymo…

多智能体融合(Multi-Agent Fusion)

多智能体融合&#xff08;Multi-Agent Fusion&#xff09;是指在多智能体系统&#xff08;MAS, Multi-Agent System&#xff09;中&#xff0c;多个智能体&#xff08;Agent&#xff09;通过协作、竞争或共享信息&#xff0c;实现全局最优的智能决策和任务执行。该方法广泛应用…

[ ] 前后端连接 结合常见故障场景和解决

调试流程图&#xff1a; 一、基础网络检查 IP与端口验证 确认前端请求的URL与后端实际运行的IP和端口完全一致&#xff08;如http://192.168.1.100:8080/api&#xff09;使用ping命令测试网络连通性&#xff0c;telnet检查端口是否开放&#xff1a; telnet 192.168.1.100 80…

EMC知识学习一

一、概念 EMC电磁兼容&#xff1a;Electromagnetic Compatibility&#xff0c;包括两个方面&#xff1a;EMI&#xff1a;electromagnetic interference电磁干扰&#xff0c;指在设备正常运行过程中对所在环境产生的干扰不能超过一定的限值&#xff0c;EMS&#xff1a;电磁耐受性…

学有所记——初探向量数据库Weaviate

目标&#xff1a; 了解向量数据库的连接、建库、插入数据、查询数据等基本用法以及关于语义相似度的一些基本概念。 背景&#xff1a; 前段时间尝试在自己的电脑上搭建OllamaDify平台&#xff0c;体验并探索大模型的强大功能。在使用过程中&#xff0c;尤其是在搭建RAG知识库…

uv包简单使用案例

uv由Charlie Marsh开发&#xff0c;是Astral Tool的一个快速Python包安装器和解析器。它类似于pip和pip-tools&#xff0c;但速度更快。此外&#xff0c;uv还支持虚拟环境管理&#xff0c;替代venv和virtualenv。 参考&#xff1a;https://github.com/astral-sh/uv 安装&#x…

34.[前端开发-JavaScript基础]Day11-王者轮播图-书籍购物车-BOM对象-JSON

1 认识BOM操作 认识BOM 2 全局对象window window对象 window对象的作用 window常见的属性 window常见的方法 3 事件对象event window常见的事件 4 location、history location对象常见的属性 Location对象常见的方法 URLSearchParams history对象常见属性和方法 5 navigato…

工作流引擎Flowable介绍及SpringBoot整合使用实例

Flowable简介 Flowable 是一个轻量级的业务流程管理&#xff08;BPM&#xff09;和工作流引擎&#xff0c;基于 Activiti 项目发展而来&#xff0c;专注于提供高性能、可扩展的工作流解决方案。它主要用于企业级应用中的流程自动化、任务管理和审批流等场景。 Flowable 的核心…

Python----计算机视觉处理(Opencv:图像边缘检测:非极大值抑制,双阈值筛选)

一、 高斯滤波 边缘检测本身属于锐化操作&#xff0c;对噪点比较敏感&#xff0c;所以需要进行平滑处理。这里使用的是一个5*5的高斯 核对图像进行消除噪声。 二、计算图像的梯度和方向 三、非极大值抑制 在得到每个边缘的方向之后&#xff0c;其实把它们连起来边缘检测就算完了…

用Deepseek写扫雷uniapp小游戏

扫雷作为Windows系统自带的经典小游戏&#xff0c;承载了许多人的童年回忆。本文将详细介绍如何使用Uniapp框架从零开始实现一个完整的扫雷游戏&#xff0c;包含核心算法、交互设计和状态管理。无论你是Uniapp初学者还是有一定经验的开发者&#xff0c;都能从本文中获得启发。 …