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

目录

前言

一、 前置说明 

二、二叉树的遍历

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. 访问效率低:链式存储无法像顺序存储那样通过下标快速访问节点。 

总结

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


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

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

相关文章

Tomcat下载,安装,配置终极版(2024)

Tomcat下载,安装,配置终极版(2024) 1. Tomcat下载和安装 进入Apache Tomcat官网,我们可以看到这样一个界面。 现在官网目前最新版是Tomcat11,我用的是Java17,在这里我们选择Tomcat10即可。Tom…

Android Studio - Android Studio 查看项目的 Android SDK 版本(4 种方式)

一、通过项目级 build.gradle 文件 1、基本介绍 在项目级 build.gradle 文件中,查看 compileSdk、minSdk、targetSdk 字段 或者是 compileSdkVersion、minSdkVersion、targetSdkVersion 字段 // 看到的可能是android {compileSdk 32defaultConfig {minSdk 21tar…

linux云服务器部署deepseek,并通过网页访问

参考视频:https://www.douyin.com/root/search/linux%E5%AE%89%E8%A3%85%20deepseek?aid3aa2527c-e4f2-4059-b724-ab81a140fa8b&modal_id7468518885570940214&typegeneral 修改ollama配置文件 vim /etc/systemd/system/ollama.service 我的电脑硬盘只有4…

【AI】mac 本地部署 Dify 实现智能体

下载 Ollama 访问 Ollama 下载页,下载对应系统 Ollama 客户端。或者参考文章【实战AI】macbook M1 本地ollama运行deepseek_m1 max可以跑deepseek吗-CSDN博客 dify 开源的 LLM 应用开发平台。提供从 Agent 构建到 AI workflow 编排、RAG 检索、模型管理等能力&am…

Jenkins介绍

什么是Jenkins Jenkins 是一个开源的自动化服务器,主要用于持续集成和持续交付(CI/CD)。它帮助开发团队自动化构建、测试和部署软件,从而提高开发效率和软件质量。 如果一个系统是前后端分离的开发模式,在集成阶段会需…

如何使用 vxe-table grid 全配置式给单元格字段格式化内容,格式化下拉选项内容

如何使用 vxe-table grid 全配置式给单元格字段格式化内容,格式化下拉选项内容 公司的业务需求是自定义配置好的数据源,通过在列中配置好数据,全 json 方式直接返回给前端渲染,不需要写任何格式化方法。 官网:https:/…

【弹性计算】IaaS 和 PaaS 类计算产品

《弹性计算产品》系列,共包含以下文章: 云服务器:实例、存储、网络、镜像、快照容器、裸金属云上运维IaaS 和 PaaS 类计算产品 😊 如果您觉得这篇文章有用 ✔️ 的话,请给博主一个一键三连 🚀&#x1f680…

【Spring详解二】容器的基本实现

二、容器的基本实现 2.1 容器的基本用法 package com.xxx; public class Hello {public void sayHello() {System.out.println("Hello, spring");} } public static void main(String[] args) {//XmlBeanFactory 在 Spring3.1 以后废弃BeanFactory beanFactory ne…

计算机毕业设计Python考研院校推荐系统 考研分数线预测 考研推荐系统 考研可视化(代码+LW文档+PPT+讲解视频)

温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…

Ubuntu 系统 LVM 逻辑卷扩容教程

Ubuntu 系统 LVM 逻辑卷扩容教程 前言 在 Linux 系统中,LVM(Logical Volume Manager)是一种逻辑卷管理工具,允许管理员动态调整磁盘空间,而无需重启系统。 本文将详细介绍如何使用 LVM 扩容逻辑卷,以实现…

大数据组件(四)快速入门实时数据湖存储系统Apache Paimon(1)

大数据组件(四)快速入门实时数据湖存储系统Apache Paimon(1) Apache Paimon 是一项流式数据湖存储技术,可以为用户提供高吞吐、低延迟的数据摄入、流式订阅以及实时查询能力。 读/写:Paimon 支持多种读/写数据和执行 OLAP 查询的方式。 对于读取&#x…

3分钟了解内外网文件传输:常见方法、注意事项有哪些?

内外网文件传输不仅是企业日常运营的基础设施,更是支持业务增长、创新和合规的关键工具。通过高效、安全的文件传输,企业能够更好地应对全球化协作、远程办公和数据安全等挑战,从而在竞争激烈的市场中保持领先地位。 一、内外网文件传输的常…

洛谷P11042 [蓝桥杯 2024 省 Java B] 类斐波那契循环数

像是这种填空题的话&#xff0c;就直接暴力还更加省时间&#xff0c;在本地算完后直接提交答案即可 #include<bits/stdc.h> using namespace std;const int N 10000000;bool isnumber(int n) {vector<int> a;int m n;while (n > 0) {a.push_back(n % 10);n / …

3月营销日历:开启春日盛宴,绽放生活魅力

关键营销节点∶惊蛰、女生节、妇女节、 植树节、315消费者权益日、春分 营销关键词 养生、女生魅力、感恩女性、环保、品质 01.重点关注品类 春季服饰&#xff1a;如轻薄外套、春装等&#xff0c;适合惊蛰后的市场需求&#xff1b; 美妆护肤&#xff1a;妇女节期间&#xf…

未来游戏:当人工智能重构虚拟世界的底层逻辑

未来游戏&#xff1a;当人工智能重构虚拟世界的底层逻辑 在《赛博朋克2077》夜之城的霓虹灯下&#xff0c;玩家或许已经注意到酒吧里NPC开始出现微表情变化&#xff1b;在《艾尔登法环》的开放世界中&#xff0c;敌人的战术包抄逐渐显露出类人智慧。这些细节预示着游戏产业正站…

知识拓展:设计模式之装饰器模式

装饰器模式拓展 1. 什么是装饰器模式&#xff1f; 装饰器模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其结构。装饰器模式通过创建一个装饰类来包装原始类&#xff0c;从而在不修…

利用 OpenCV 进行棋盘检测与透视变换

利用 OpenCV 进行棋盘检测与透视变换 1. 引言 在计算机视觉领域&#xff0c;棋盘检测与透视变换是一个常见的任务&#xff0c;广泛应用于 摄像机标定、文档扫描、增强现实&#xff08;AR&#xff09; 等场景。本篇文章将详细介绍如何使用 OpenCV 进行 棋盘检测&#xff0c;并…

MySQL智障离谱问题,删了库确还存在、也不能再创建同名库

1、问题 今天跟后端朋友接毕设单子的时候&#xff0c;后端穿过来的【weather.sql】这个文件没弄好&#xff0c;导致这个【weather】数据库的数据是错的&#xff0c;因此我用datagrip的GUI界面直接右键删除&#xff0c;结果就是tmd删不掉&#xff0c;ok&#xff0c;我只能在那新…

高子昂医编---23岁,医疗编上岸,正式开启养老生活

作为一个只想毕业后就找个稳定工作躺平一生的普通人&#xff0c;直接放弃加入考研考公大军&#xff0c;加入了竞争稍微小一点的考编大军&#xff01;毕业那年在学校辛苦奋斗四个多月&#xff0c;直接一战上岸&#xff01;成为了一名有编制的医务工作者&#xff01;现在在我们家…

【Linux系统】—— 调试器 gdb/cgdb的使用

【Linux系统】—— 调试器 gdb/cgdb的使用 1 前置准备2 快速认识 gdb3 cgdb/gdb 的使用3.1 简单认识 cgdb3.2 打断点 / 删断点3.3 逐过程 / 逐语句3.4 查看变量3.5 快速跳转 4 cgdb/gdb 调试技巧4.1 watch4.2 「set var」确定问题原因4.3 条件断点 5 概念理解6 gdb/cgdb 指令一…