当前位置: 首页 > article >正文

二叉树链式结构:数据结构中的灵动之舞

目录

前言

一、 前置说明 

二、二叉树的遍历

2.1前序遍历

 2.2中序遍历

 2.3 后序遍历

2.4层序遍历

 三、二叉树的遍历的应用

  3.1二叉树节点个数:

 3.2二叉树的高度

3.3 二叉树第k层的节点的个数

 3.4二叉树的查找

总结


前言

在数据结构的世界里,二叉树是一种极其重要的结构,它以其独特的性质和广泛的应用场景而备受关注。二叉树的存储结构主要有两种:顺序存储和链式存储。今天,我们将深入探讨二叉树的链式存储结构,从其基本概念、实现方式到实际,应用帮助大家全面理解这一强大的数据结构。


一、 前置说明 

在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。由于现在对二叉树结构还不够深入,为了降低学习成本,此处手动快速创建一棵简单的二叉树,快速进入二叉树操作学习,等二叉树结构了解的差不多时,我们反过头再来研究二叉树真正的创建方式。

typedef int BTDataType;
typedef struct BinaryTreeNode
{BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;BTNode* Buynode(BTDataType x)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));if (node == NULL){perror("malloc fail");exit(1);}node->data = x;node->left = NULL;node->right = NULL;return node;
}
BTNode* CreatTree()
{BTNode* node1 = Buynode(1);BTNode* node2 = Buynode(2);BTNode* node3 = Buynode(3);BTNode* node4 = Buynode(4);BTNode* node5 = Buynode(5);BTNode* node6 = Buynode(6);node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;return node1;
}

 在看二叉树基本操作前,首先要对二叉树的基本结构要清楚

  • 空树
  • 非空:根节点,根节点的左子树、根节点的右子树组成的。

 可以看出,二叉树的定义是递归式的,所以后续基本操作中基本都是按递归实现的,当然也是以二叉树的基本结构:根,左子树,右子树为基本展开的


二、二叉树的遍历

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

 二叉树的遍历有:前序/中序/后序的递归结构遍历

2.1前序遍历

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

 前序遍历(根-左-右)

void PreOrder(BTNode* root)//前序遍历
{if (root == NULL){printf("NULL ");return;}printf("%d ", root->data);PreOrder(root->left);PreOrder(root->right);
}

  递归演示图:

 OR

void PreOrder(BTNode* root)//前序遍历
{if (root){printf("%d ", root->data);PreOrder(root->left);PreOrder(root->right);}
}

 递归演示图:

 

 2.2中序遍历

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

中序遍历(左-根-右)

void InOrder(BTNode* root)//中序遍历
{if (root == NULL){printf("NULL ");return;}InOrder(root->left);printf("%d ", root->data);InOrder(root->right);}

 2.3 后序遍历

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

 后序遍历(左-右-根)

void PostOrder(BTNode* root)//后序遍历
{if (root == NULL){printf("NULL ");return;}PostOrder(root->left);PostOrder(root->right);printf("%d ", root->data);}

测试一下:

 

2.4层序遍历

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

 

 要用到队列的结构,A节点进对列出队列的时候,把a节点的左右子数节点进队列,a节点的左子数节点就是b,然后b再出队列的时候,又把b的左右子数节点路队列,然后c节点出队列,再又把c节点的左右子数节点路队列,以此类推

void LevelOrder(BTNode* root)//层序遍历
{Queue q;Init(&q);if(root)Push(&q, root);while (!Empty(&q)){BTNode*front = QueueFront(&q);Pop(&q);printf("%d ",front->data);if (front->left){Push(&q, front->left);}if (front->right){Push(&q, front->right);}}Destroy(&q);
}

需注意的是,存入队列的是指向节点的指针,因此,需改变一下队列存储的数据类型:

typedef struct BinaryTreeNode* c;
typedef struct QueueNode
{struct QueueNode* next;c data;
}QNode;

 测试一下:


 三、二叉树的遍历的应用

  3.1二叉树节点个数:

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

 当然还有其他的方法 如:

定义一个全局变量size,然后用递归遍历树,每次能成功递归一次,就加一

int size = 0;
void TreeSize(BTNode* root)
{if (root == NULL)return;size++;TreeSize(root->left);TreeSize(root->right);}
int main()
{BTNode* root = CreatTree();PreOrder(root);printf("\n");InOrder(root);printf("\n");PostOrder(root);printf("\n");TreeSize(root);printf("TreeSize=%d ",size);TreeSize(root);printf("\n");printf("TreeSize=%d ", size);return 0;
}

但是最好不要用全局变量,而且每次统计数量时,还要初始化一次,要不然就会

int size = 0;
void TreeSize(BTNode* root)
{if (root == NULL)return;size++;TreeSize(root->left);TreeSize(root->right);}
int main()
{BTNode* root = CreatTree();PreOrder(root);printf("\n");InOrder(root);printf("\n");PostOrder(root);printf("\n");TreeSize(root);printf("TreeSize=%d ",size);TreeSize(root);printf("\n");printf("TreeSize=%d ", size);return 0;
}

 如果是改为局部的静态,甚至都没法初始化它,局部的静态只会在第一次调用它的时候初始化,

void TreeSize(BTNode* root)
{static int size = 0;if (root == NULL)return;size++;TreeSize(root->left);TreeSize(root->right);}

 最好的方式就是在TreeSize函数中增添一个变量psize,统计它的个数,想要形参影响实参,就要传指针,就要传地址:

void TreeSize(BTNode* root,int *psize)
{static int size = 0;if (root == NULL)return;(*psize)++;TreeSize(root->left,psize);TreeSize(root->right,psize);
}
int main()
{BTNode* root = CreatTree();PreOrder(root);printf("\n");InOrder(root);printf("\n");PostOrder(root);printf("\n");int size1 = 0;TreeSize(root,&size1);printf("TreeSize=%d ",size1);int size2 = 0;TreeSize(root,&size2);printf("\n");printf("TreeSize=%d ", size2);return 0;
}

 测试一下:


 3.2二叉树的高度

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

 用参数保留它递归的结果,这样就不用return的时候再递归

3.3 二叉树第k层的节点的个数

int TreeKevel(BTNode* root, int x)
{assert(x > 0);if (root == NULL)return 0;if (x == 1)return x;/* int leftK = TreeKevel(root->left, x - 1);int rightK = TreeKevel(root->right, x - 1); return leftK+rightK+1   or*/return TreeKevel(root->left, x - 1) + TreeKevel(root->right, x - 1);
}

 3.4二叉树的查找

BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL || root->data == x)return root;BTNode* Findleft = BinaryTreeFind(root->left, x);if (Findleft)return Findleft;BTNode* Findright = BinaryTreeFind(root->right, x);if (Findright)return Findright;return NULL;
}

 我一开始在写的时候只写出了这样的代码,后来我画图演示递归的过程,才发现的错误,所以说要多画图

BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL || root->data == x)return root;BTNode* Findleft = BinaryTreeFind(root->left, x);if (Findleft)return Findleft;BTNode* Findright = BinaryTreeFind(root->right, x);if (Findright)return Findright;
}

 查找递归演示图:


链式结构的优缺点

  • 优点1. 灵活性高:可以方便地表示任意形状的二叉树。
  • 2. 操作方便:插入、删除等操作只需修改指针,无需移动大量数据。
  • 3. 内存使用灵活:可以根据需要动态分配内存。
  • 缺点1. 空间开销大:每个节点需要额外存储两个指针,增加了内存开销。
  • 2. 访问效率低:链式存储无法像顺序存储那样通过下标快速访问节点。 

总结

二叉树的链式存储结构是一种强大而灵活的存储方式。它适用于各种复杂的二叉树操作,尤其是在处理非满二叉树时表现出色。通过本文的介绍,希望大家对二叉树的链式存储结构有了更深入的理解。在实际应用中,可以根据具体需求选择合适的存储方式,充分发挥链式结构的优势。


相关文章:

二叉树链式结构:数据结构中的灵动之舞

目录 前言 一、 前置说明 二、二叉树的遍历 2.1前序遍历 2.2中序遍历 2.3 后序遍历 2.4层序遍历 三、二叉树的遍历的应用 3.1二叉树节点个数: 3.2二叉树的高度 3.3 二叉树第k层的节点的个数 3.4二叉树的查找 总结 前言 在数据结构的世界里,二叉…...

【kafka系列】Kafka如何保证消息不丢失?

目录 1. 生产者端:确保消息成功发送到Broker 核心机制: 关键步骤: 2. Broker端:持久化与副本同步 核心机制: 关键源码逻辑: 3. 消费者端:可靠消费与Offset提交 核心机制: 关…...

新建github操作

1.在github.com的主页根据提示新建一个depository。 2.配置用户名和邮箱 git config --global user.name "name" git config --global user.email "email" 3.生成ssh秘钥 ssh-keygen -t rsa 找到public key 对应的文件路径 cat /root/.ssh/id_rsa 复制显…...

第 15 天:数据存储,打造存档 读取系统!

🎯 目标: ✅ 掌握 UE5 SaveGame 存档系统 ✅ 在 C 创建存档类,存储游戏数据 ✅ 实现存档 & 读取功能,让游戏状态可持久化 ✅ 在 BP_PlayerCharacter 里实现: * 游戏开始时自动加载存档 * 玩家受到伤害时自动存档 …...

Flutter 异步编程利器:Future 与 Stream 深度解析

目录 一、Future:处理单次异步操作 1. 概念解读 2. 使用场景 3. 基本用法 3.1 创建 Future 3.2 使用 then 消费 Future 3.3 特性 二、Stream:处理连续异步事件流 1. 概念解读 2. 使用场景 3. 基本用法 3.1 创建 Stream 3.2 监听 Stream 3.…...

Java短信验证功能简单使用

注册登录阿里云官网&#xff1a;https://www.aliyun.com/ 搜索短信服务 自己一步步申请就可以了 开发文档&#xff1a; https://next.api.aliyun.com/api-tools/sdk/Dysmsapi?version2017-05-25&languagejava-tea&tabprimer-doc 1.引入依赖 <dependency>…...

React进阶之React核心源码解析(一)

React核心源码解析 react 特点CPU卡顿IO 卡顿 新老 react 架构对比v15v16.8Scheduler 调度器Reconciler 协调器 React fiber原理更新dommount 构建过程 render阶段 — scheduler reconcilerreact源码解析react-domreact-dom/src/client/ReactDOMRoot.js react-reconcilerreact-…...

【Vue】打包vue3+vite项目发布到github page的完整过程

文章目录 第一步&#xff1a;打包第二步&#xff1a;github仓库设置第三步&#xff1a;安装插件gh-pages第四步&#xff1a;两个配置第五步&#xff1a;上传github其他问题1. 路由2.待补充 参考文章&#xff1a; 环境&#xff1a; vue3vite windows11&#xff08;使用终端即可&…...

类加载机制及双亲委派模型

一、引言 二、类加载流程 1. 加载 2. 连接 2.1 验证 2.2 准备 2.3 解析 3. 初始化 三、类加载器 类加载器的类型 双亲委派模型 打破双亲委派模型 双亲委派模型优点 一、引言 在 Java 的运行机制中&#xff0c;类加载是一个至关重要的环节。它不仅决定了 Java 程序的动态…...

tcp/ip协议设置参数,tcp/ip协议6设置

TCP/IP协议设置参数主要涉及到IP地址、子网掩码、网关地址以及DNS服务器地址等关键参数。这些参数的配置确保了网络设备能够正确地接入互联网并与其他设备进行通信。以下是对这些参数设置的详细说明&#xff1a; 1. IP地址 定义&#xff1a;IP地址是互联网中用于唯一标识每一…...

如何在Java EE中使用标签库?

在Java EE&#xff08;现在称为Jakarta EE&#xff09;中使用标签库&#xff08;Tag Library&#xff09;&#xff0c;主要是通过JSP标准标签库&#xff08;JSTL&#xff09;或自定义标签库来实现的。标签库允许在JSP页面中使用自定义的标签&#xff0c;从而简化页面逻辑、增强…...

【java】方法的基本内存原理(栈和堆)

java内存主要分为栈和堆&#xff0c;方法相关的部分主要在栈内存里&#xff0c;每个方法调用时会在栈里创建一个栈帧&#xff0c;存放局部变量和方法执行的信息。执行完后栈帧被销毁&#xff0c;局部变量消失。而对象实例存在堆里&#xff0c;由垃圾回收器管理。 **Java方法内…...

今日AI和商界事件(2025-02-15)

根据2025年2月15日的科技动态&#xff0c;以下是今日AI领域的重要事件及相关进展总结&#xff1a; 1. DeepSeek日活突破3000万&#xff0c;开源生态加速AI普惠 里程碑意义&#xff1a;开源大模型DeepSeek宣布日活跃用户数突破3000万&#xff0c;其R1模型凭借开源策略和低成本优…...

尚硅谷课程【笔记】——大数据之Hadoop【一】

课程视频链接&#xff1a;尚硅谷Hadoop3.x教程 一、大数据概论 1&#xff09;大数据概念 大数据&#xff08;Big Data&#xff09;&#xff1a;指无法再一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合&#xff0c;是需要新处理模式才能具有更强的决策力、洞察发…...

SQL 建表语句详解

SQL 建表语句详解 在 SQL 中&#xff0c;创建表&#xff08;Table&#xff09;是数据库设计的基础。表是存储数据的基本单位&#xff0c;每个表由行和列组成。创建表的过程涉及到定义表的结构&#xff0c;包括列名、数据类型、约束等。本文将详细介绍 SQL 中的建表语句&#x…...

wordpress主题插件开发中高频使用的38个函数

核心模板函数 get_header()/get_footer()/get_sidebar() – 加载模板部件 the_title()/the_content()/the_excerpt() – 显示文章标题、内容、摘要 the_post() – 循环中获取文章数据 bloginfo(‘url’) – 获取站点URL wp_head()/wp_footer() – 输出头部/尾部代码 wp_n…...

DockerFile优化镜像体积

title: DockerFile优化镜像体积 date: 2025-02-15 15:22:40 tags: DockerFile优化镜像体积DockerFile优化镜像体积 DockerFile优化镜像体积前文回顾:一、细数优化镜像体积的思路与方式二、优化Dockfile文件编辑 Dockerfile2文件三、构建镜像四、运行镜像五、查看运行效果原文 …...

使用 playwright 自定义 js 下载的路径和文件名

遇到一个问题&#xff0c;点击按钮自动下载文件&#xff0c;路径和文件名都不能自定义&#xff0c;可以用 playwright 来解决这个问题 from playwright.sync_api import sync_playwright import os import time class ExcelDownloader: def __init__(self, download_pat…...

Open FPV VTX开源之OSD使用分类

Open FPV VTX开源之OSD使用分类 1. 源由2. 硬件2.1 【天空端】SigmaStar2.2 【天空端】Raspberry Pi2.3 【地面端】 3. 软件3.1 天空端软件3.2 地面端软件 4. 分类4.1 嵌入式OSD分类A1-嵌入式OSD&#xff1a;SigmaStar Android分类A2-嵌入式OSD&#xff1a;SigmaStar Hi3536分…...

题解:洛谷 P4113 [HEOI2012] 采花

题目https://www.luogu.com.cn/problem/P4113 运用类似于P1972 [SDOI2009] HH的项链的操作&#xff0c;将数据离线下来处理。 按照区间右端点从小到大排序。 问题是数量大于等于 的时候才能算进去。 于是乎我们用两个数组维护倒数第二次出现和最后一次出现的地方。 每次在…...

linux概念详解

用户守护进程 用户空间守护进程是一些在后台运行的长期服务程序&#xff0c;提供系统级服务。 下面举一些例子。 网络服务&#xff1a; 如sshd&#xff08;SSH服务&#xff09;、httpd&#xff08;HTTP服务&#xff09;。 sshd&#xff1a;sshd 守护进程会在后台运行&#x…...

easyexcel快速使用

1.easyexcel EasyExcel是一个基于ava的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel 即通过java完成对excel的读写操作&#xff0c; 上传下载 2.easyexcel写操作 把java类中的对象写入到excel表格中 步骤 1.引入依赖 <depen…...

fetch() 与 XMLHttpRequest 的差异

fetch() 与 XMLHttpRequest 的差异 fetch() 的功能与 XMLHttpRequest 基本相同&#xff0c;都是向服务器发出 HTTP 请求&#xff0c;但有三个主要的差异。 &#xff08;1&#xff09;fetch()使用 Promise&#xff0c;不使用回调函数&#xff0c;因此大大简化了写法&#xff0…...

【java面向对象的三大特性】封装、继承和多态

目录标题 一、封装&#xff08;Encapsulation&#xff09;&#xff1a;二、继承&#xff08;Inheritance&#xff09;&#xff1a;三、多态&#xff08;Polymorphism&#xff09;&#xff1a;1. 多态的三个必要条件&#xff1a;2.多态的具体实现&#xff1a;3.多态的使用场景&a…...

c# textbox 设置不获取光标

[DllImport("user32",EntryPoint "HideCaret")] private static extern bool HideCaret(IntPtr hWnd); //需引入命名空间using System.Runtime.InteropServices; private void Txt_RecInfo_MouseDown(object sender, MouseEventArgs e) { …...

算法13-BFPRT算法

一、BFPRT 算法概念 BFPRT 算法&#xff08;Blum-Floyd-Pratt-Rivest-Tarjan 算法&#xff09;是一种用于在无序数组中快速找到第 k 小&#xff08;或第 k 大&#xff09;元素的高效算法。它的时间复杂度为 O(n)&#xff0c;在最坏情况下也能保证线性时间复杂度。BFPRT 算法的…...

android studio下载安装汉化-Flutter安装

1、下载android studio官方地址&#xff1a;&#xff08;这个网址可能直接打不开&#xff0c;需要VPN&#xff09; https://developer.android.com/studio?hlzh-cn mac版本分为X86和arm版本&#xff0c;电脑显示芯片是Inter的就是x86的&#xff0c;显示m1和m2的就是arm的 …...

Seaweedfs(master volume filer) docker run参数帮助文档

文章目录 进入容器后执行获取weed -h英文中文 weed server -h英文中文 weed volume -h英文中文 关键点测试了一下&#xff0c;这个-volume.minFreeSpace string有点狠&#xff0c;比如设置值为10&#xff08;10%&#xff09;&#xff0c;它直接给系统只留下10%的空间&#xff0…...

嵌套调用实现数组元素逆序存放

主函数调用reverse_array(int ptr[],int cnt)函数&#xff0c;该函数在调用inplace_swap(int *x,int *y)函数时&#xff0c;把两个不同的地址送给inplace_swap(int *x,int *y)函数&#xff0c;实现这两个位置处元素的交换。 令*xa,*yb 则*y *x^*y执行后&#xff0c;*xa,*ya^b…...

【工业安全】-CVE-2022-35555- Tenda W6路由器 命令注入漏洞

文章目录 1.漏洞描述 2.环境搭建 3.漏洞复现 4.漏洞分析 4.1&#xff1a;代码分析  4.2&#xff1a;流量分析 5.poc代码&#xff1a; 1.漏洞描述 漏洞编号&#xff1a;CVE-2022-35555 漏洞名称&#xff1a;Tenda W6 命令注入 威胁等级&#xff1a;高危 漏洞详情&#xff1…...