二叉树:从基础结构到高级遍历技术

news2024/9/24 23:06:26

在这里插入图片描述
在这里插入图片描述

.

个人主页:晓风飞
专栏:数据结构|Linux|C语言
路漫漫其修远兮,吾将上下而求索


文章目录

  • 引言
  • 结构定义
  • 接口需求
  • 构建二叉树
  • 销毁二叉树
  • 计算节点和叶子的数量
    • 二叉树节点个数
    • 二叉树叶子节点个数
    • 二叉树第k层节点个数
  • 二叉树查找值为x的节点
  • 二叉树的遍历
    • 二叉树前序遍历
    • 二叉树中序遍历
    • 二叉树后序遍历
    • 二叉树层序遍历
  • 深度优先遍历(DFS)
  • 广度优先遍历(BFS)
  • 判断二叉树是否是完全二叉树
  • 实际应用
  • 完整代码
  • 结论


引言

在数据结构中,二叉树因其在搜索、排序和平衡等操作中的高效性而显得尤为重要。在这篇博客文章中,我们将深入探讨二叉树的概念,探索它们在C语言中的实现,并了解它们的实际应用。

结构定义

在C语言中,可以使用结构来表示二叉树,定义数据以及指向左右子节点的指针:

typedef char BTDataType;

typedef struct BinaryTreeNode
{
  BTDataType _data;
  struct BinaryTreeNode* _left;
  struct BinaryTreeNode* _right;
}BTNode;

接口需求

// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi);
// 二叉树销毁
void BinaryTreeDestory(BTNode** root);
// 二叉树节点个数
int BinaryTreeSize(BTNode* root);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root);
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root);
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root);

构建二叉树

//通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{
  if (a[(*pi)] == '#'  || *pi > n)  //判断是否遍历完成或者遇到#,都返回NULL
  {
    (*pi)++;
    return NULL;
  }
 
  BTNode* root = (BTNode*)malloc(sizeof(BTNode)); // 创建新节点
  root->_data = a[*pi]; // 设置节点数据
  (*pi)++;
  root->_left = BinaryTreeCreate(a, n, pi); // 递归构建左子树
  root->_right = BinaryTreeCreate(a, n, pi); // 递归构建右子树
  return root;
}

销毁二叉树

// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{	if(*root == NULL)
		return NULL;
	BinaryTreeDestory(*root->left);
	BinaryTreeDestory(*root->right);
	free(*root);
}

计算节点和叶子的数量

二叉树节点个数

// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
  return root == NULL ? 0 : BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right) + 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);
}

二叉树第k层节点个数

在这里插入图片描述

// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
  if (root == NULL || k < 1)
    return 0;
  if (k == 1)
    return 1;

  return BinaryTreeLevelKSize(root->_left, k - 1) + BinaryTreeLevelKSize(root->_right, k - 1);
}

二叉树查找值为x的节点

// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
  
  // 如果当前节点为空,返回 NULL。这意味着我们已经到达树的末端或者树本身为空。
  if (root == NULL)
    return NULL;

  // 如果当前节点的值等于要查找的值 x,返回当前节点的指针。
  if (root->_data == x)
    printf("找到了它是:%c",root->_data);
  else
    printf("没有找到!", root->_data);
    return root;

  // 在左子树中递归查找值为 x 的节点。
  BTNode* leftResult = BinaryTreeFind(root->_left, x);

  // 如果在左子树中找到了这个值,返回该节点的指针。
  if (leftResult != NULL)
    return leftResult;

  // 如果左子树中没有找到,继续在右子树中递归查找。
  return BinaryTreeFind(root->_right, x);
}

二叉树的遍历

遍历是二叉树中的一个关键操作,允许我们按特定顺序访问每个节点:
在这里插入图片描述

前序遍历:遍历顺序是根节点,左子树,右子树。
中序遍历:遍历顺序是左子树,根节点,右子树。
后序遍历:遍历顺序是左子树,右子树,根节点。

二叉树前序遍历

// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root)
{
    if (root == NULL)
      return NULL;

    printf("%c ", root->_data);
    BinaryTreePrevOrder(root->_left);
    BinaryTreePrevOrder(root->_right);
}

前序遍历首先访问根节点,然后递归地遍历左子树和右子树。这种遍历方法首先处理当前节点,然后处理子节点。

二叉树中序遍历

// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
    if (root == NULL)
      return NULL;

    BinaryTreeInOrder(root->_left);
    printf("%c ", root->_data);
    BinaryTreeInOrder(root->_right);
}

中序遍历首先递归地遍历左子树,然后访问根节点,最后遍历右子树。这对于二叉搜索树来说,可以按顺序访问所有节点。

二叉树后序遍历

// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{
    if (root == NULL)
      return NULL;
    BinaryTreePostOrder(root->_left);
    BinaryTreePostOrder(root->_right);
    printf("%c ", root->_data);
}

后序遍历先递归地遍历左子树和右子树,最后访问根节点。这种遍历方法通常用于先处理子节点,然后再处理父节点的场景,如在二叉树中释放或删除节点。

二叉树层序遍历

// 二叉树层序遍历
void BinaryTreeLevelOrder(BTNode* root) 
{
  if (root == NULL) 
    return; // 如果树为空,则直接返回
 
  Queue q;
  QInit(&q); // 初始化队列
  QPush(&q, root); // 将根节点入队

  while (!QueueEmpty(&q))
  {
    BTNode* front = QueueFront(&q); // 取得队首元素
    QPop(&q); // 将队首元素出队

    // 处理当前节点,例如打印节点的值
    printf("%c ", front->_data);

    if (front->_left != NULL) 
    {
      QPush(&q, front->_left); // 将左子节点入队
    }
    if (front->_right != NULL) 
    {
      QPush(&q, front->_right); // 将右子节点入队
    }
  }

  QDestroy(&q); // 销毁队列,释放资源
}

层序遍历使用队列来迭代地访问每一层的所有节点,从根节点开始,依次遍历每一层的左右子节点。这种遍历方法可以确保按照树的层次顺序访问每个节点,从上到下,从左到右。

在这里插入图片描述

深度优先遍历(DFS)

遍历方式:DFS 从根节点开始,沿着树的深度向下遍历,直到没有子节点可以访问,然
后回溯到上一个节点,继续遍历未探索的分支。这个过程一直重复,直到访问了所有可达的节点。

使用的数据结构:通常使用栈(Stack)来实现,这可以是递归实现的隐式栈(函数调用栈)或显式的数据结构栈。

特点:DFS 能够深入到树的底部,但可能会在树的某一分支上“走得太远”,尤其是在处理具有大量节点和深层次结构的树时。

应用场景:DFS 更适用于目标路径较深或树的结构不规则的情况。在图论中,DFS 常用于拓扑排序和检查图中的环。

以下是遍历顺序:
在这里插入图片描述

广度优先遍历(BFS)

遍历方式:BFS 从根节点开始,先遍历根节点的所有邻接节点,然后再遍历这些邻接节点的邻接节点,以此类推。BFS 逐层遍历树的节点,从根节点开始向外扩散。

使用的数据结构:通常使用队列(Queue)来实现。遍历过程中,先进入队列的节点先遍历,确保了节点是按照层级顺序访问的。

特点:BFS 能够快速访问到从根节点开始的每一层节点,但在宽度较大的树或图中,可能会消耗更多的内存。

应用场景:BFS 更适用于目标节点离根节点比较近或需要按层次遍历的情况。在图论中,BFS 常用于寻找最短路径。

总结来说,DFS 优先沿着树的深度遍历,直到遇到叶子节点,然后回溯并探索其他分支,而 BFS 优先遍历距离根节点最近的节点,逐层向外扩展。两者在解决不同类型的问题时各有优势。DFS 在树的深度较大时更有效,而 BFS 在寻找最短路径或树的宽度较大时更有优势。

以下是遍历顺序
在这里插入图片描述


判断二叉树是否是完全二叉树

// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root) {
  if (root == NULL) {
    return 1; // 空树认为是完全二叉树
  }

  Queue q;
  QInit(&q);
  QPush(&q, root);
  int level = 0; // 记录层级
  bool mustBeLeaf = false; // 标记是否必须为叶子节点

  while (!QueueEmpty(&q)) {
    int size = QueueSize(&q); // 当前层的节点数
    level++;
    for (int i = 0; i < size; i++) {
      BTNode* front = QueueFront(&q);
      QPop(&q);

      // 如果标记为必须是叶子节点,但当前节点不是叶子节点,则不是完全二叉树
      if (mustBeLeaf && (front->_left != NULL || front->_right != NULL)) {
        return 0;
      }

      // 如果当前节点有右孩子但没有左孩子,则不是完全二叉树
      if (front->_left == NULL && front->_right != NULL) {
        return 0;
      }

      if (front->_left != NULL) {
        QPush(&q, front->_left);
      }
      else {
        // 如果左孩子为空,则后面的节点都必须是叶子节点
        mustBeLeaf = true;
      }

      if (front->_right != NULL) {
        QPush(&q, front->_right);
      }
      else {
        // 如果右孩子为空,则后面的节点都必须是叶子节点
        mustBeLeaf = true;
      }
    }
  }

  QDestroy(&q);
  return 1; // 遍历结束没有发现违反完全二叉树的规则,返回1
}


这个函数用来判断一个二叉树是否是完全二叉树。它通过层序遍历来实现,检查每个节点是否符合完全二叉树的性质:如果某个节点的右子节点存在,而左子节点不存在,则不是完全二叉树;如果某个节点的左子节点或右子节点不存在,则该节点后的所有节点必须是叶子节点。

实际应用

二叉树不仅仅是理论构造,它们在现实世界中有着广泛的应用:

数据库系统:在索引中使用,实现高效的数据检索。
文件系统:用于组织文件的层级结构。
网络路由算法:帮助优化数据包的传输路径。

完整代码

可以来我的github参观参观,看完整代码
路径点击这里–>数据结构二叉树的实现

结论

通过深入了解和实现二叉树,我们不仅能够提高我们的编程技能,还能更好地理解这些结构在现代计算中的重要性。无论是在学术研究还是实际应用中,二叉树都是一个值得掌握的关键概念。

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

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

相关文章

Dhcp中继华为+虚拟机

拓扑图&#xff1a; 配置二层交换机sw1 创建vlan 将e0/0/1接口封装到vlan10 将e0/0/2接口封装到vlan20 将e0/0/4接口封装到vlan100 将e0/0/3接口配置为中继口 并允许所有vlan通过 配置三层交换机Lsw1 将e0/0/1接口配置中继口 并允许所有vlan通过 开启dhcp 创建vlan 10 20 100 进…

前端学习路径

菜鸟感觉很多人不太知道菜鸟写的博客是一个可以跟着学习、一起深入理解的过程&#xff0c;其中包括了菜鸟从刚开始学习到后面重新学习&#xff0c;再到后面进入学框架等一系列学习过程、知识和感悟&#xff0c;所以菜鸟把自己的博客整理成一个目录提取出来&#xff0c;好让读者…

使用vue快速开发一个带弹窗的Chrome插件

vue-chrome-extension-quickstart 说在前面 &#x1f388;平时我们使用Chrome插件通常都只是用来编写简单的js注入脚本&#xff0c;大家有没有遇到过需要插件在页面上注入一个弹窗呢&#xff1f;比如我们希望可以通过快捷键快速唤起ChatGPT面板或者快速唤起一个翻译面板&#x…

汽车线束的汽配企业MES管理系统解决方案

随着科技的飞速发展和环保需求的日益提升&#xff0c;新能源汽车在全球范围内崭露头角&#xff0c;成为未来出行的主导力量。在这股浪潮中&#xff0c;中国凭借其强大的研发实力和市场敏锐度&#xff0c;迅速崛起为新能源汽车领域的佼佼者。而作为汽车数字化控制与智能化应用的…

openpyxl绘制图表

嘿&#xff0c;你是不是在处理Excel文件时感到束手无策&#xff1f;是不是想要一个简单而又强大的工具来处理数据分析和图表制作&#xff1f;别担心&#xff0c;我们有解决方案&#xff01;让我向你介绍openpyxl&#xff0c;这是一个Python库&#xff0c;专门用于处理Excel文件…

从车联网到智慧城市:智慧交通的革新之路

一、引言 1、智慧城市的概念和发展背景 智慧城市&#xff08;Smart City&#xff09;是指以信息技术为基础&#xff0c;运用信息与通信等手段&#xff0c;对城市各个核心系统各项关键数据进行感测、分析、整合和利用&#xff0c;实现对城市生活环境的感知、资源的调控&#x…

基于Ubuntu22.04部署生产级K8S集群v1.27(规划和核心组件部署篇)

本文档主要根据k8s官网文档和其插件的官网文档&#xff0c;参考部分他人优秀经验&#xff0c;在实际操作中逐渐完成&#xff0c;比较详尽&#xff0c;适合在境内学习者和实践者参考。 实操环境基于VMware Workstation 17 pro&#xff0c;采用ubuntu22.04操作系统&#xff08;有…

MATLAB运行simulink模型显示找不到库Failed to load library

MATLAB运行simulink模型显示找不到库Failed to load library ‘ 原因 上述的错误即提示加载某一个库失败了&#xff0c;原因就是MATLAB需要在其设定的set path中寻找。 设置 paths 查看paths 添加成功后再次打开MATLAB的set path&#xff0c;可以看到相关文件及库被添加进来…

身体互联网 (IoB)

现在&#xff0c;我们的互联网网关就是我们手中的一个小设备。 普渡大学副教授施里亚斯森表示。 我们不断地看着这个盒子&#xff0c;我们低着头走路&#xff0c;我们把大部分时间都花在它上面。如果我们不想让这种未来继续下去&#xff0c;我们就需要开发新技术。相反&#x…

K8s:Pod生命周期

我们一般将pod对象从创建至终的这段时间范围称为pod的生命周期&#xff0c;它主要包含下面的过程&#xff1a; pod创建过程 运行初始化容器&#xff08;init container&#xff09;过程 运行主容器&#xff08;main container&#xff09; 容器启动后钩子&#xff08;post st…

Uibot (RPA设计软件)Mage AI智能识别(发票识别)———课前材料五

微信群发助手机器人的小项目友友们可以参考小北的课前材料二博客~ (本博客中会有部分课程ppt截屏,如有侵权请及请及时与小北我取得联系~&#xff09; 紧接着小北的前两篇博客&#xff0c;友友们我们即将开展新课的学习~RPA 培训前期准备指南——安装Uibot(RPA设计软件&#x…

Python展示 RGB立方体的二维切面视图

代码实现 import numpy as np import matplotlib.pyplot as plt# 生成 24-bit 全彩 RGB 立方体 def generate_rgb_cube():# 初始化一个 256x256x256 的三维数组rgb_cube np.zeros((256, 256, 256, 3), dtypenp.uint8)# 填充立方体for r in range(256):for g in range(256):fo…

积萨伯爵潮流品牌,全品类国际化,新营销创业掀起创业新浪潮

近年来中古消费开始在国内广泛兴起、受众逐渐扩大&#xff0c;尤其是在这几年疫情的影响下&#xff0c;借助直播带货的东风&#xff0c;二奢经济逐渐实现了从小众向大众的实力破圈。在二奢经济大潮下&#xff0c;一股新型创业风正刮起。 95后、00后为代表的Z时代年轻人渐渐成为…

压缩编码之JPEG变换编码不同压缩率的模拟的实现——数字图像处理

原理 离散余弦变换&#xff08;DCT&#xff09;和量化是图像压缩中的两个关键步骤&#xff0c;尤其是在JPEG压缩标准中。 离散余弦变换&#xff08;DCT&#xff09;&#xff1a;DCT的目的是将图像从空间域&#xff08;即像素表示&#xff09;转换到频率域。这种转换后&#x…

单例模式实现最好的方式即枚举实现

单例类作为23种设计模式当中最常用的设计模式&#xff0c;实现方式有很多种&#xff0c;比较流行的是DCL(DoubleCheckLock)双重检查的实现&#xff0c;线程安全&#xff0c;又比较好&#xff0c;除了存在序列化的问题之外&#xff0c;还算不错&#xff0c;如果对DCL模式还不熟悉…

修改和调试 onnx 模型

1. onnx 底层实现原理 1.1 onnx 的存储格式 ONNX 在底层是用 Protobuf 定义的。Protobuf&#xff0c;全称 Protocol Buffer&#xff0c;是 Google 提出的一套表示和序列化数据的机制。使用 Protobuf 时&#xff0c;用户需要先写一份数据定义文件&#xff0c;再根据这份定义文…

用Python创建轻量级Excel到Markdown转换工具:简便、高效、自动化【第28篇—python:Excel到Markdown】

文章目录 用 Python 创建 Excel 转 Markdown 的 CLI 工具设计思路Excel 文件结构解析读取 Excel 文件解析表格数据生成 Markdown 表格完整代码1. 参数化文件路径&#xff1a;2. 处理不同的工作表&#xff1a;3. 改进 Markdown 表格生成&#xff1a;4. 错误处理&#xff1a;5. 打…

Alist开源网盘搭建

官网&#xff1a;https://alist.nn.ci/zh/github下载地址&#xff1a;https://github.com/alist-org/alist/releases gitcode上也提供了源码:https://gitcode.com/mirrors/alist-org/alist/tags 源码安装使用自己研究,这里不讲解,较为复杂 我使⽤的版本:v3.29.1 我的下载地址:…

uniapp使用Android Studio离线打包

环境准备 Android Studio&#xff1a; 下载地址APP离线SDK下载&#xff1a; 下载地址; 目前我使用得是“Android-SDK3.8.7.81902_20230704”&#xff1b;需要与hbuider版本配套使用。Appkey: 参考我 以上三步准备好后&#xff0c;进行接下来的不住&#xff1a; 准备工程 导…

刷题 ------ 二分枚举(查找)

文章目录 1.x 的平方根2.第一个错误的版本3.有效的完全平方数4.猜数字大小5.排列硬币6. 寻找比目标字母大的最小字母7. 二分查找8.检查整数以及其两倍数是否存在9. 两个数组间的距离值10.特殊的数组的特征值11.找出数组排序后的目标下标12.和有限的最长子序列13.正整数和负数的…