【数据结构】穿梭在二叉树的时间隧道:顺序存储的实现

专栏引入

哈喽大家好,我是野生的编程萌新,首先感谢大家的观看。数据结构的学习者大多有这样的想法:数据结构很重要,一定要学好,但数据结构比较抽象,有些算法理解起来很困难,学的很累。我想让大家知道的是:数据结构非常有趣,很多算法是智慧的结晶,我希望大家在学习数据结构的过程是一种愉悦的心情感受。因此我开创了《数据结构》专栏,在这里我将把数据结构内容以有趣易懂的方式展现给大家。

1.二叉树的顺序存储结构 

之前我们谈过了树的存储结构,并且谈到了顺序存储结构对树这种一对多的关系结构实现起来还是比较困难的。但二叉树是一种特殊的树,由于二叉树的特殊性,使得它可以使用顺序存储结构来实现,二叉树的顺序存储结构就是使用一维数组存储二叉树中的节点,并且节点的存储位置,也就是数组的下标要能体现出来节点之间的逻辑关系,比如:双亲和孩子的关系、左孩子右兄弟的关系等。先来看看完全二叉树的顺序存储,就用下面这棵二叉树为例:

将这棵二叉树存入数组中,相应的下标对应其同样的位置,很多数据结构相关书籍上下标都是将0空置,从1开始存储,其实下标0的位置是否存放数据对堆的实现的难度没有影响,为了节省空间我对下标为0的位置进行了存储,如下图:

这下我们可以看出来完全二叉树的优越性了吧,由于它严格的定义,所以用顺序结构也可以表现出二叉树的结构来,当然对于一般的二叉树,尽管层序编号不能反映出来逻辑关系,但是可以将其按完全二叉树来编号,只不过,把不存在的节点设置为"NULL"而已,就像下面的图中,虚线部分表示不存在:

 

 

我们再考虑一种极端的情况:一颗深度为h的右斜树。它只有h个节点,却要分配2^{h}-1个存储单元空间,这显然是对存储空间的浪费,如下图所示:

 所以,二叉树的顺序存储结构一般只适用于完全二叉树。上一篇中我们提到了堆是一个特殊的完全二叉树,所以这篇我们就以堆为例子来实现二叉树的顺序存储。

2.堆

2.1堆的概念

堆是具有下列性质的完全二叉树:每个结点的值都大于或等于其左右孩子节点的值,称为大顶堆;或者每个结点的值都小于等于其左右孩子结点的值,称为小顶堆。如下图所示:

从堆的定义可以知道:根节点一定是堆中所有结点的最大(小)值 。在上一篇堆二叉树的性质介绍时,有一个性质还没和大家介绍,因为这个性质就仿佛是为堆量身定制的,所以我计划在介绍堆时再介绍它:

如果一棵有n个节点的完全二叉树(其深度为\left \lfloor \log_{2}n \right \rfloor+1)的节点按层序编号(从第一层到第\left \lfloor \log_{2}n \right \rfloor+1层,每层从左到右),对任一节点i(1≤i≤n)有:

  • 如果i=1,则节点i是二叉树的根,无双亲;如果i>1,则双亲是节点\left \lfloor i/2 \right \rfloor
  • 如果2i>n,则节点i没有左孩子(节点i为叶子节点),否则其左孩子是节点2i。
  • 如果2i+1>n,则节点i没有右孩子;否则其右孩子是节点2i+1。

在这个性质第二、三条,也就是说明下标i与2i和2i+1的双亲子女的关系:双亲结点=(子节点-1)/2。

2.2堆的实现

我们先来定义一下堆的结构:

typedef int HPDataType;
typedef struct Heap
{HPDataType* a;int size;int capacity;
}HP;

2.2.1堆的创建和销毁

堆的创建我们使用顺序存储来实现,所以它的创建和销毁的实现代码和顺序表的实现的相同。首先是堆的创建函数:

void HeapInit(HP* php)
{assert(php);php->a = NULL;php->size = 0;php->capacity = 0;
}

接着是堆的销毁函数:

void HeapDestroy(HP* php)
{assert(php);free(php->a);php->a = NULL;php->size = php->capacity = 0;
}

2.2.2堆的向上调整

一说到调整我们肯定会对两个数据进行交换,我们先来写一个交换函数:

void Swap(HPDataType* p1, HPDataType* p2)
{HPDataType tmp = *p1;*p1 = *p2;*p2 = tmp;
}

堆的向上调整是将一个元素插入到小堆时,调整堆的结构,使其满足小堆的性质过程。实现堆的向上调整的要先将元素插入到数组的最后一个位置(这一步我们在堆的插入操作中实现)。这时候我们就要比较插入元素和其双亲结点的大小关系,然后做出调整。这里我们可以用循环实现,用循环来实现比用递归实现的好处有:

  1. 空间开销:循环实现通常比递归实现需要更少的内存空间。递归函数会在每次调用时创建一个新的函数栈帧,而循环则只使用一个循环体内的局部变量。
  2. 性能:循环实现通常比递归实现具有更高的执行效率。递归函数在每次调用时都需要进行函数调用、参数传递和返回值处理等操作,而循环通过使用迭代变量和循环条件判断来控制流程,避免了递归带来的额外开销。

首先我们先将这个函数里面的判断条件先写好:

void AdjustUp(HPDataType* a, int child)
{int parent = (child - 1) / 2;while (){if (a[child] < a[parent]){Swap(&a[child], &a[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}

既然我们要用循环来实现,那么使用while()语句的循环条件是什么呢?最坏的情况就是将新插入的节点经过调整变成根节点,那条件是parent≥0吗?parent是不会小于0的,当parent=0时,插入的数据还要接着向上调整交换,此时child变为0,parent=(0-1)/2,我们计算出来的parent时-0.5,但是要取整,所以parent还是0,此时循环还是没结束,会再次进入循环,此时child==parent,经过判断语句会侥幸跳出循环,这样是不严谨的。我们将循环条件设为child大于0就可以完美解决这个问题。此时,这个向上调整的操作完整代码为:

void AdjustUp(HPDataType* a, int child)
{int parent = (child - 1) / 2;while (child > 0){if (a[child] < a[parent]){Swap(&a[child], &a[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}

2.3堆的插入 

 堆的插入操作和顺序表的插入操作相似,其具体步骤为:

  1. 首先,函数开始时先进行一些边界判断,确保堆的指针有效。
  2. 如果堆的当前元素数量等于堆的容量,即堆已满,需要进行动态扩容。这里使用realloc函数对堆的数组进行扩容,新的容量为原来容量的两倍。
  3. 将待插入的元素x放到堆数组的最后一个位置,并将堆的元素数量递增。
  4. 调用AdjustUp函数对刚插入的元素进行向上调整,保持堆的性质。

我们来实现一下这个操作:

void HeapPush(HP* php, HPDataType x)
{assert(php);if (php->size == php->capacity){int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* tmp = (HPDataType*)realloc(php->a, newCapacity * sizeof(HPDataType));if (tmp == NULL){perror("realloc fail");exit(-1);}php->a = tmp;php->capacity = newCapacity;}php->a[php->size] = x;php->size++;AdjustUp(php->a, php->size - 1);
}

2.4堆的向下调整

堆的向上调整是为了让堆在插入数据时能够保持堆的性质,而堆的向下调整操作是在删除根节点后,为了维护堆的性质而进行的操作。该操作的目的是将新的根节点下沉到合适的位置,使得根节点的值不大于它的左右子节点的值。堆的向下调整具体步骤为:

  1. 初始化子节点的索引child,设为父节点的左孩子节点的索引,即parent*2+1。
  2. 进入一个循环,判断当前节点是否有左孩子节点,如果有则执行循环体。
  3. 在循环体内,先假设左孩子节点的值最小,将child设为左孩子节点的索引。
  4. 检查右孩子节点是否存在,即child+1<size,如果存在且右孩子节点的值小于左孩子节点的值,则将child更新为右孩子节点的索引。
  5. 检查当前节点的值是否大于最小的子节点的值,如果是,则交换当前节点和最小子节点的值,将当前节点的索引设为子节点的索引child,并更新子节点的索引为新的左孩子节点的索引child=parent*2+1。
  6. 如果当前节点的值不大于最小子节点的值,即a[child]>=a[parent],则跳出循环。
  7. 重复之前的步骤,直到当前节点没有左孩子节点为止。

我们来具体实现一下这个操作:

void AdjustDown(int* a, int size, int parent)
{int child = parent * 2 + 1;while (child < size){// 假设左孩子小,如果解设错了,更新一下if (child+1 < size && a[child + 1] < a[child]){++child;}if (a[child] < a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}

 2.5堆的删除

堆的删除具体操作步骤为:

  1. 调用Swap函数来交换小堆根节点(php->a[0])和最后一个节点(php->a[php->size - 1])的值。这样就将要删除的根节点移到了最后一个位置。
  2. 将小堆的大小减1,即php->size--,表示删除了一个节点。
  3. 最后,调用AdjustDown函数来对小堆进行向下调整,以维护小堆的性质。这样就完成了小堆的删除操作。

我们来实现一下这个操作:

void HeapPop(HP* php)
{assert(php);assert(php->size > 0);Swap(&php->a[0], &php->a[php->size - 1]);php->size--;AdjustDown(php->a, php->size, 0);
}

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

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

相关文章

CDN(Content Delivery Network)内容分发网络原理、组成、访问过程、动静态加速、作用详解

CDN简介 什么是CND CDN&#xff08;Content Delivery Network&#xff09;的缩写&#xff0c;是一种利用分布式节点技术&#xff0c;在全球部署服务器&#xff0c;即时地将网站、应用视频、音频等静态或动态资源内容分发到用户所在的最近节点&#xff0c;提高用户访问这些内容…

PDF分页处理:技术与实践

引言 在数字化办公和学习中&#xff0c;PDF文件因其便携性和格式稳定性而广受欢迎。然而&#xff0c;处理大型PDF文件时&#xff0c;我们经常需要将其拆分成单独的页面&#xff0c;以便于管理和分享。本文将探讨如何使用Python编程语言和一些流行的库来实现PDF文件的分页处理。…

管道液位传感器在蒸汽清洗机的应用

管道光电液位传感器采用了先进的光学感应原理&#xff0c;无需机械运动&#xff0c;具有长久的寿命和稳定的性能。与传统的机械式和电容式传感器相比&#xff0c;管道光电液位传感器具有更高的精度和可靠性&#xff0c;解决了机械式传感器容易卡死失效和电容式传感器感度衰减的…

数据结构之初始泛型

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a;数据结构&#xff08;Java版&#xff09; 目录 深入了解包装类 包装类的由来 装箱与拆箱 面试题 泛型 泛型的语法与使用…

springboot+elementui健康饮食系统

此系统是springboot健康饮食管理平台 得简化版&#xff0c;适合期末大作业 系统包括 管理员端和用户端 1.用户端注册即可登录到用户端&#xff0c;用户端包括首页轮播图&#xff0c;以及个人中心&#xff0c;个人信息修改&#xff0c;头像修改&#xff0c;后台根据用户信息&am…

构造+割点,F2. Spanning Tree with One Fixed Degree

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 Problem - 1133F2 - Codeforces 二、解题报告 1、思路分析 考虑以根节点为割点&#xff0c;会有若干个连通块 连通块的数目为根节点至少要连出去的边&#xff0c;不妨记为mi 如果mi > D&#xff0c;那…

mysql终端使用中的错误

在这个过程中&#xff0c;出现了几个问题&#xff1a; 在退出 MySQL 后&#xff0c;你尝试再次使用 mysql 命令登录&#xff0c;但系统提示找不到该命令。这可能是因为 MySQL 的执行文件路径没有加入到系统的环境变量中。你可以尝试使用绝对路径来运行 mysql 命令&#xff0c;或…

mac环境基于llama3和metaGPT自动开发2048游戏

1.准备虚拟环境 conda create -n metagpt python3.9 && conda activate metagpt 2.安装metagpt pip install --upgrade metagpt 3.初始化配置文件 metagpt --init-config 4. 安装llama3 5. 修改配置文件 6.让metegpt自动开发2048游戏 7.经过多轮迭代&#xff0c;最终…

个人网站建设方案书

个人网站建设方案书 一、项目背景 随着互联网的迅猛发展&#xff0c;个人网站已经成为展示个人能力、情感表达的重要平台。无论是个人品牌推广&#xff0c;还是个人作品展示&#xff0c;个人网站都能够为个人提供一个独特的展示空间。因此&#xff0c;建设一个个人网站已经成为…

深度神经网络——什么是 CNN(卷积神经网络)?

Facebook和Instagram自动检测图像中的面孔&#xff0c;Google通过上传照片搜索相似图片的功能&#xff0c;这些都是计算机视觉技术的实例&#xff0c;它们背后的核心技术是卷积神经网络&#xff08;CNN&#xff09;。那么&#xff0c;CNN究竟是什么呢&#xff1f;接下来&#x…

HTB 靶场 Mailing 未完待续

访问网页 在/etc/hosts 添加ip和域名 hosts 文件包含ip地址与主机名之间的映射&#xff0c;还包括主机的别名。 Linux系统所有程序查询/etc/hosts文件解析对主机名或者域名的IP地址。没有找到就需要使用DNS服务器解释域名。 DNS原理 1 输入域名&#xff0c;在本地缓存服务…

新手教程之使用LLaMa-Factory微调LLaMa3

文章目录 为什么要用LLaMa-Factory什么是LLaMa-FactoryLLaMa-Factory环境搭建微调LLaMA3参考博文 为什么要用LLaMa-Factory 如果你尝试过微调大模型&#xff0c;你就会知道&#xff0c;大模型的环境配置是非常繁琐的&#xff0c;需要安装大量的第三方库和依赖&#xff0c;甚至…

【数据结构与算法 经典例题】链表的回文结构(图文详解)

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《数据结构与算法 经典例题》C语言 期待您的关注 ​ 目录 一、问题描述 二、解题思路 三、C语言代码实现 一、问题描述 二、解…

深度学习-07-反向传播的自动化

深度学习-07-反向传播的自动化 本文是《深度学习入门2-自製框架》 的学习笔记&#xff0c;记录自己学习心得&#xff0c;以及对重点知识的理解。如果内容对你有帮助&#xff0c;请支持正版&#xff0c;去购买正版书籍&#xff0c;支持正版书籍不仅是尊重作者的辛勤劳动&#xf…

OJ题目【栈和队列】

题目导入 栈&#xff1a; 题目一&#xff1a;有效的括号题目二&#xff1a;用栈实现队列 队列 题目&#xff1a;实现循环队列 栈 题目一 有效的括号 题目要求 给定一个只包括 ‘(’&#xff0c;‘)’&#xff0c;‘{’&#xff0c;‘}’&#xff0c;‘[’&#xff0c;‘…

LeetCode746使用最小花费爬楼梯

题目描述 给你一个整数数组 cost &#xff0c;其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用&#xff0c;即可选择向上爬一个或者两个台阶。你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。请你计算并返回达到楼梯顶部的最低花费。 解析 动态…

七校联赛题铅笔姿态及笔迹检测装置--mpu6050识别数字

前言 前几天打完比赛&#xff0c;收获还是挺大的&#xff0c;数字识别部分基本上浪费了绝大部分时间。先将思路简单说明。 1、题目 2、思路 针对笔迹检测&#xff0c;我们首先考虑的肯定是陀螺仪来测量加速度方向来判断书写的方向&#xff0c;从而得到书写的数字。 我们的方案…

LLM的基础模型3:Transformer变种

大模型技术论文不断&#xff0c;每个月总会新增上千篇。本专栏精选论文重点解读&#xff0c;主题还是围绕着行业实践和工程量产。若在某个环节出现卡点&#xff0c;可以回到大模型必备腔调或者LLM背后的基础模型新阅读。而最新科技&#xff08;Mamba,xLSTM,KAN&#xff09;则提…

【软件测试】自动化测试如何管理测试数据

前言 在之前的自动化测试框架相关文章中&#xff0c;无论是接口自动化还是UI自动化&#xff0c;都谈及data模块和config模块&#xff0c;也就是测试数据和配置文件。 随着自动化用例的不断增加&#xff0c;需要维护的测试数据也会越来越多&#xff0c;维护成本越来越高&#…

编译 freetype、sdl、sdl_ttf

/* * three lib */ /* 字符串生成位图 */ http://www.libsdl.org/projects/SDL_ttf/release/ http://www.libsdl.org/download-1.2.php https://freetype.org/ /* Freetype 2.13.0 */ ./configure CCaarch64-v01c01-linux-gnu-gcc --buildaarch64 …