【树与二叉树】二叉树链式结构及实现--万字详解介绍(上)

简介: 【树与二叉树】二叉树链式结构及实现--万字详解介绍(上)

一、链式存储:

在之前的博客中,我们说过,二叉树的存储结构一般可以简单地分为顺序存储结构与链式存储结构,今天我们将要进行研究的,就是其实中的链式存储结构。

二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。

链式存储结构又可以分为二叉链与三叉链:

typedef int BTDataType;
// 二叉链
struct BinaryTreeNode
{
    struct BinTreeNode* left; // 指向当前节点左孩子
    struct BinTreeNode* right; // 指向当前节点右孩子
    BTDataType data; // 当前节点值域
}
// 三叉链
struct BinaryTreeNode
{
    struct BinTreeNode* parent; // 指向当前节点的双亲
    struct BinTreeNode* left; // 指向当前节点左孩子
    struct BinTreeNode* right; // 指向当前节点右孩子
    BTDataType data; // 当前节点值域
};

我们今天要研究的,是其中的二叉链部分,而三叉链的部分将来在研究红黑树时详细学习。普通二叉树的增删查改复杂且没有意义,所以我们并不打算学习它的增删查改,主要是学习它的结构。

二、链式结构的遍历:

学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历(Traversal)是指:按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。同时,遍历也是二叉树上最重要的运算之一,是二叉树上进行其它运算的基础。


1.前序、中序与后序遍历:

按照规则,二叉树的遍历有:前序、中序与后序的递归结构遍历,其规则如下:

前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。

中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之间。

后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。

// 二叉树前序遍历
void PreOrder(BTNode* root);
// 二叉树中序遍历
void InOrder(BTNode* root);
// 二叉树后序遍历
void PostOrder(BTNode* root);

由于被访问的结点必是某子树的,所以 N(Node)、L(Left subtree)和 R(Right subtree)又可解释为根、根的左子树和根的右子树。NLR、LNR 和 LRN 分别又称为先根遍历中根遍历后根遍历

1.1 概念选择题

利用已知限有条件构建二叉树

1.某完全二叉树按层次输出(同一层从左到右)的序列为 ABCDEFGH 。该完全二叉树的前序序列为( )

A. ABDHECFG

B. ABCDEFGH

C. HDBEAFCG

D. HDEBFGCA

分析

bc8971159f084f188e87436968cb9c54.png

所以选择 A 选项

2.二叉树的先序遍历和中序遍历如下:先序遍历:EFHIGJK; 中序遍历:HFIEJKG; 则二叉树根结点为( )

A. E

B. F

C. G

D. H

7ae73ce284fa4692bda63ee950713b70.png

  • 分析
    根据这棵树的先序遍厉、中序遍厉可以重建这棵树。但其实这里并不需要重建,因为先序遍厉是从根开始的。

所以选择 A 选项

3.设—课二叉树的中序遍历序列: badce,后序遍历序列: bdeca, 则二叉树前序遍历序列为( )

A. adbce

B. decab

C. debac

D. abcde

分析

255576ab11fc48bdb18e813f4726d329.png根据这棵树的后序遍厉、中序遍厉可以重建这棵树。

所以选择 D 选项

某二叉树的后序遍历序列与中序遍历序列相同,均为 ABCDEF,

4.某二叉树的后序遍历序列与中序遍历序列相同,均为 ABCDEF,则按层次输出 (同一层从左到右) 的序列为( )

A. FEDCBA

B. CBAFED

C. DEFCBA

D. ABCDEF

  • 分析

显然这道题作为选择题来说一眼就能知道答案:根据它的后序遍厉知道根是 F4e51dd6bc72e4ced8f3394aa2ecfbf2e.png

所以选择 A 选项

总结:想要构建或还原一棵树,需要知道1.前序遍历序列+中序遍历序列 2.中序遍历序列+后序遍历序列 //前序+后序是无法还原的,因为不能判断左右子树的顺序

2.层序遍历:

除了最常用的先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在层数为 1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第 2 层上的节点,接着是第三层的节点,以此类推。而这种自上而下,自左至右逐层访问树的结点的过程就是层序遍历。

3.DFS(深度)与BFS(广度)

深度和广度,其实指的是:

1.深度优先遍厉:二叉树的前序遍历、中序遍历、后序遍历->一般用递归

注意 有些说法只认同前序遍历,看具体如何定义

2. 广度优先遍厉:二叉树的层序遍历->一般用队列

eg.扫雷 DFS->八路遍历 , BFS->一圈一圈往外出

三、各接口功能实现:

1.创建二叉树结构:

首先创建一个二叉树节点的结构体类型,然后通过根节点对这个二叉树进行操作

typedef char BDataType;
typedef struct BinaryTreeNode
{
  struct BinaryTreeNode* left; // 指向当前节点左孩子
  struct BinaryTreeNode* right; // 指向当前节点右孩子
  BDataType data; // 当前节点值域
}BNode;

2.创建二叉树节点:

节点的创建只需要动态开辟一个空间,用于存放我们节点的值,再将左右指针置空,并返回创建好的节点的地址

BNode* BuyNode(BDataType x)
{
  BNode* node = (BNode*)malloc(sizeof(BNode));
  if (node == NULL)
  {
    printf("malloc Error!\n");
    return;
  }
  node->data = x;
  node->left = NULL;
  node->right = NULL;
  return node;
}

3.前序遍历:

执行操作前需进行非空判断,防止对空指针进行操作。

对于前序遍历的操作原理,我们可以结合这张示意图来理解:

image.png这个接口的实现方式(访问顺序)为:先访问根节点,即当前节点的值,接着递归访问左子树,最后递归访问右子树。使用递归实现整个二叉树的遍历,而不使用循环语句。

void PrevOrder(BNode* root)
{
  if (root == NULL)
  {
    printf("PrevOrder Error!\n");
    return;
  }
  printf("%c ", root->data); // 访问当前节点的值
  PrevOrder(root->left); // 先递归访问当前节点的左子树
  PrevOrder(root->right); // 再递归访问当中前节点的右子树
}

4.中序遍历:

执行操作前需进行非空判断,防止对空指针进行操作。

同样我们结合操作原理示意图来理解:

网络异常,图片无法展示
|

这个接口的实现方式(访问顺序)为:先递归访问左树,再访问节点自身,最后递归访问右树。中序遍历同样使用递归实现整个二叉树的遍历,而不使用循环语句。

void InOrder(BNode* root)
{
  if (root == NULL)
  {
    printf("InOrder Error!\n");
    return;
  }
  InOrder(root->left); // 递归访问当前节点的左树
  printf("%c ", root->data); // 访问当前节点的值
  InOrder(root->right); // 最后递归访问当前节点的右树
}

5.后序遍历:

执行操作前需进行非空判断,防止对空指针进行操作。

后序遍历操作原理示意图:

网络异常,图片无法展示
|
后序遍历接口的实现方式(访问顺序)为:先递归访问左子树,再递归访问右子树,最后访问节点自身。后续遍历也使用 递归实现整个二叉树的遍历,而不使用循环语句。

void PostOrder(BNode* root)
{
  if (root == NULL)
  {
    printf("PostQrder Error!\n");
    return;
  }
  PostOrder(root->left); // 先递归访问左子树
  PostOrder(root->right); // 再递归访问右子树
  printf("%c ", root->data); // 最后访问当前节点的值
}
相关文章
|
Shell Linux 计算机视觉
【Dlib】动作检测:以常见的人脸识别验证为例讲解张嘴与闭眼
【Dlib】动作检测:以常见的人脸识别验证为例讲解张嘴与闭眼
823 0
|
2月前
|
SQL XML 自然语言处理
Text2SQL 破局技术解析之一:规范文本与灵活性
润乾NLQ创新采用“规范文本”作为中间层,将自然语言转SQL分为三阶段:LLM生成可读的规范文本,用户确认意图后,通过规则引擎转为MQL再生成准确SQL。该方案兼顾灵活性、准确性与复杂查询支持,大幅降低企业实施成本,为人机协同的Text2SQL提供了可行的工程化路径。
|
2月前
|
人工智能 移动开发 自然语言处理
2025 AI 数字人应用典型案例 TOP5:多场景实战范本与价值解析
AI数字人迈向规模化应用,2025年落地政务、国企、文旅、医疗、职教五大领域。世优波塔五大案例展现跨行业实践:北京丰台智慧政务、陕建集团智能供应链、伊犁将军府沉浸导览、南阳医院智慧导诊、天津轻工“鲁班工坊”多语接待,构建可复制的数字化转型新范式。
511 0
2025 AI 数字人应用典型案例 TOP5:多场景实战范本与价值解析
|
10月前
|
传感器 人工智能 物联网
健康监测设备的技术革命:AI+物联网如何让你随时掌握健康数据?
健康监测设备的技术革命:AI+物联网如何让你随时掌握健康数据?
1285 19
|
10月前
|
机器学习/深度学习 人工智能 自然语言处理
DeepMesh:3D建模革命!清华团队让AI自动优化拓扑,1秒生成工业级网格
DeepMesh 是由清华大学和南洋理工大学联合开发的 3D 网格生成框架,基于强化学习和自回归变换器,能够生成高质量的 3D 网格,适用于虚拟环境构建、动态内容生成、角色动画等多种场景。
885 4
DeepMesh:3D建模革命!清华团队让AI自动优化拓扑,1秒生成工业级网格
|
传感器 存储 Ubuntu
Azure Kinect DK + ROS1 Noetic使用教程
本文是Azure Kinect DK在Ubuntu20.04下配合ROS1 Noetic使用的教程,内容包括一键安装脚本、硬件介绍、安装SDK相关软件包、设置Udev规则、SDK基本测试、DK ROS基本测试,以及存在的一些重要缺陷和相关参考文献。教程详细指导了如何配置和使用Azure Kinect DK,提供了安装步骤和解决常见问题的方法。
1226 1
Azure Kinect DK + ROS1 Noetic使用教程
|
前端开发 开发者 异构计算
CSS进阶-CSS动画关键帧
【6月更文挑战第15天】CSS的`@keyframes`创建细腻动画,定义样式变化阶段以增强网页互动性。通过`animation`属性应用动画,如`fadeIn`示例。常见问题包括动画结束状态、卡顿和浏览器兼容性,解决办法涉及优化关键帧、使用硬件加速和添加前缀。进阶技巧包括多步骤动画和控制播放状态。例如,背景色渐变动画展示了颜色随时间变化的效果。学习和实践关键帧动画,提升Web开发技能。
516 7
|
存储 JSON OLAP
Hologres支持哪些数据格式?
【8月更文挑战第20天】Hologres支持哪些数据格式?
412 1
|
存储 Java Maven
【JavaEE进阶】 Maven jar 包下载失败问题的解决方法
【JavaEE进阶】 Maven jar 包下载失败问题的解决方法
|
存储 Python
炸金花底层模拟
炸金花底层模拟
750 0