数据结构——顺序二叉树——堆

news2024/11/20 13:33:18

1.树的相关概念

        在介绍二叉树之前,我们首先要明确树是什么。

        树用我们的通常认识来判断应该是一种植物,从根向上生长,分出许多的树枝并长出叶子。对于数据结构中的树而言,其结构也正是从树的特征中剥离出来的。树结构是一种非线性数据结构,具有一个根结点,这一个根节点连接着其他若干个结点,这些结点也同样可以连接这其他若干个结点,如此形成的数据结构我们就称为树。

        子树即树的子集,我们可以认为根节点延伸出的若干个节点实际上是若干棵子树。注意,在树形结构的定义下子树之间不允许交集的存在,即树中不允许存在环。

        一个结点或树具有一些性质,一个结点的指的是其所含有的子树的个数,而一个树的度指的是这棵树的结点中最大的度;一个结点的层次指的是其对于根节点而言所处的层数,而一个树的高度/深度指的是这棵树的结点中最大的层次。

        按照结点的度和层次特点,可以分为:根节点——第一层;叶子结点——度为0;分支结点——度不为0;根据我们生活中的伦理关系延伸,一个结点的前驱结点称为父结点,后继结点称为子节点,同一个父结点的结点称为兄弟节点

2.二叉树相关概念与存储结构

2.1 二叉树概念

        二叉树是一种特殊的树。满足度为2的树就是二叉树,所以对于任意一个二叉树的节点我们都可以将其视为是一个根节点连接着左右两个子树的结构。

        

        满二叉树是一种特殊的二叉树,其每一层结点的个数都是满的。因此对于一个n层的满二叉树,其结点个数为2^k-1个。

        完全二叉树也是一种特殊的二叉树,其与和满二叉树相似,结点按层依次排序,到所有结点连接前不允许出现空位。

         值得注意的是:二叉树的度为0的结点个数一定比度为2的结点个数多一个

2.2二叉树的存储结构

2.2.1 顺序结构

        顺序结构采取数组来存储二叉树。数组根据二叉树按层的顺序将每个结点的值存进数组的对应位置。对于一棵完全二叉树,其父子结点之间在下标上存在固定的递推关系式,对于一个根节点位于下标为0位置的树而言:左孩子下标=父结点下标*2+1右孩子下标=父结点下标*2+2父结点下标=孩子结点下标/2。根据这个规律,我们便可以在数组中存下所有的二叉树结点并可以找到结点相关联的其它结点。

        可见,顺序二叉树按位置以此存储,这就意味着如果不是完全二叉树,那么在数组中就会出现空位。因此顺序二叉树一般只会用于完全二叉树来避免空间浪费。

2.2.2 链式结构

        链式二叉树使用链表来表示,链就作为二叉树的逻辑关系指示。链式二叉树结点存储着自己的数据和两个指针,指针分别指向左孩子和右孩子,由此组成最后的完整的二叉树。链式二叉树可以存储任意形式的二叉树,我们一般采用二叉链。

2.堆

2.1 堆的概念

        对于一个集合k,将其所有元素按照顺序二叉树的方式存入一个一维数组,对任意元素k_{i},若满足k_{i}\leq k_{2*i+1}k_{i} \leq k_{2*i+2}则为小堆;若满足k_{i} \geq k_{2*i+1}k_{i} \geq k_{2*i+2}则为大堆。

        通俗解释来说:堆是一个完全二叉树,当所有的父结点都小于等于自己的子结点时,构成了小堆;当所有的父结点都大于等于自己的子结点时,构成了大堆。

 2.2 堆的工程

2.2.1 堆的定义

        因为我们已经明确了,堆是使用数组进行存储,所以堆的实现方式应该和顺序表相同。

typedef int HPDataType;

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

 2.2.2 堆的函数接口

 2.2.2.1 堆的初始化与销毁

        堆的初始化与销毁和顺序表相同,不再过多叙述。

void HeapInit(HP* php)
{
	assert(php);

	php->data = NULL;
	php->capacity = php->size = 0;
}

void HeapDestroy(HP* php)
{
	assert(php);

	free(php->data);
	php->data = NULL;
	php->capacity = php->size = 0;
}
2.2.2.2 堆的大小、判空、取堆顶元素

        这些都是很基础的操作,与栈和队列相似,也不再详细说道。

int HeapSize(HP* php)
{
	assert(php);

	return php->size;
}

bool HeapEmpty(HP* php)
{
	assert(php);

	return php->size == 0;
}

HPDataType HeapTop(HP* php)
{
	assert(php);
	assert(php->size > 0);

	return php->data[php->size - 1];
}

2.2.3 堆的数据插入——向上调整

        对于一个堆,当我们插入一个数据的时候,这个数据会被插入到数组的尾部,也即二叉树的下一个结点处。但是我们的堆可不是那么随便的结构,小堆大堆数据之间有着自己特定的结构,这样直接在后面插入数据有可能会破坏其原有的结构,所以我们需要考虑如何再插入数据后保持其堆的特性。为此我们引入了向上调整算法

        向上调整算法目的是将新入堆的元素位置与原堆中的元素位置进行调整,使得再次构成一个堆。

        对于小堆而言,向上调整就是将子结点与其父亲相比,如果孩子小于父亲,则将二者位置调换,循环往复直到满足堆的条件为止。对于大堆而言,向上调整就是将子结点与其父亲相比,如果孩子大于父亲,则将二者位置调换,循环往复直到满足堆的条件为止。在调整过程中,每次交换后根据父子结点下标关系可以找到新的父结点与子结点。小堆和大堆的向上调整算法只在if语句中判断条件不同。

void AdjustUpMin(int* arr, int size)
{
	int child = size - 1;
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (arr[child] < arr[parent])
		{
			swap(&arr[child], &arr[parent]);
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
void AdjustUpMax(int* arr, int size)
{
	int child = size - 1;
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (arr[child] > arr[parent])
		{
			swap(&arr[child], &arr[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

        当有了向上调整算法后,我们就可以进行数据插入了。

void HeapPush(HP* php, HPDataType x)
{
	assert(php);

	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;
		HPDataType* tmp = realloc(php->data, sizeof(int) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		else
		{
			php->data = tmp;
		}
		php->capacity = newcapacity;
	}

	php->data[php->size] = x;
	php->size++;
	AdjustUp(php);
}

 2.2.4 堆的数据删除——向下调整

        对于堆来说,堆顶的元素是具有特殊意义的。小堆的堆顶是最小的,大队的堆顶是最大的。所以在对堆进行数据删除时,我们删除数组最后的元素意义不大,我们考虑的是取出堆顶元素后,针对元素堆顶进行删除。同理堆删除数据也会导致堆的结构被破坏,所以需要我们需要想办法恢复其堆的特性。这就需要我们设计向下调整算法

        向下调整算法的目的是将堆顶的元素与原堆中的元素交换位置,使得可以继续满足原来小堆或大堆的特征。

        对于小堆而言,向下调整是将其与子结点相比较,因为父结点需要小于子结点,所以我们需要选取左右孩子结点中较小的一个。为此我们可以假设左孩子是较小者,如果右孩子比左孩子小说明假设错误,让child+1即可。之后判断父结点与子结点的大小关系,如果满足小堆的要求就结束,否则将二者调换位置,然后找到新的父结点和子结点下标。如此循环,即可调整成为小堆。而对于大堆,和小堆同理,只是判断标准变成了父结点需要大于子节点,若父结点小于子节点则需要调整位置。

void AdjustDownMin(int* arr, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		if (child + 1 < size && arr[child] > arr[child + 1])
		{
			child++;
		}
		if (arr[child] < arr[parent])
		{
			swap(&arr[child], &arr[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}

	}
}
void AdjustDownMax(int* arr, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		if (child + 1 < size && arr[child] < arr[child + 1])
		{
			child++;
		}
		if (arr[child] > arr[parent])
		{
			swap(&arr[child], &arr[parent]);
			parent = child;
			child = child * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

        于是乎,我们就可以写出数据删除的函数,这里是将堆顶元素删除,然后用最后一个元素代替堆顶元素,进行向下调整。

void HeapPop(HP* php)
{
	assert(php);

	Swap(&php->data[0], &php->data[php->size - 1]);
	php->size--;
	AdjustDown(php);
}

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

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

相关文章

求斐波那契数列矩阵乘法的方法

斐波那契数列 先来简单介绍一下斐波那契数列&#xff1a; 斐波那契数列是指这样一个数列&#xff1a;1&#xff0c;1&#xff0c;2&#xff0c;3&#xff0c;5&#xff0c;8&#xff0c;13&#xff0c;21&#xff0c;34&#xff0c;55&#xff0c;89……这个数列从第3项开始 &…

Scratch优秀作品飞翔小鸟

程序说明&#xff1a;在无尽的划痕堆中飞驰而过随着你越来越多地飞进迷宫般的街区&#xff0c;平台变得越来越难。 演示视频 scratch飞翔小鸟 其实这就是一个类似像素小鸟的程序&#xff0c;只不过水管角色就地取材&#xff0c;使用scratch里面的积木图片拼成了水管&#xff0…

【算法】了解哈希表/思想 并用哈希解算法题(C++)

文章目录 基本了解解题1.两数之和面试题01.02.判定是否互为字符重排217.存在重复元素219.存在重复元素II49.字母异位词分组 基本了解 哈希表是什么&#xff1f; 一种数据结构&#xff0c;用于存储元素。 有什么用&#xff1f; 用于快速查找元素 与 插入 何时用哈希表&…

代码随想录 Leetcode160. 相交链表

题目&#xff1a; 代码(首刷看解析 2024年1月13日&#xff09;&#xff1a; class Solution { public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {ListNode *A headA, *B headB;while (A ! B) {A A ! nullptr ? A->next : headB;B B ! nullpt…

Shell编程自动化之Shell数学运算与条件测试

一、Shell数学运算 1.Shell常见的算术运算符号 序号算术运算符号意义1、-、*、/、%加、减、乘、除、取余2**幂运算3、–自增或自减4&&、||、&#xff01;与、或、非5、!相等、不相等&#xff0c;也可写成6、、-、*、/、%赋值运算符&#xff0c;a1相等于aa1 2.Shell常…

HUAWEI华为MateStation S台式机电脑12代PUC-H7621N,H5621N原装出厂Windows11.22H2系统

链接&#xff1a;https://pan.baidu.com/s/1QtjLyGTwMZgYiBO5bUVPYg?pwd8mx0 提取码&#xff1a;8mx0 原厂WIN11系统自带所有驱动、出厂主题壁纸、系统属性专属联机支持标志、Office办公软件、华为电脑管家等预装程序 文件格式&#xff1a;esd/wim/swm 安装方式&#xf…

高光谱分类论文解读分享之基于形态卷积神经网络的高光谱影像分类

IEEE TGRS 2021&#xff1a;基于形态卷积神经网络的高光谱影像分类 题目 Morphological Convolutional Neural Networks for Hyperspectral Image Classification 作者 Swalpa Kumar Roy; Ranjan Mondal; Mercedes E. Paoletti; Juan M. Haut; Antonio Plaza 关键词 Clas…

Ubuntu下使用Virtual Box中显示没有可用的USB设备

Ubuntu中使用Virtual Box&#xff0c;但是使用到USB时只有USB1.1可以使用&#xff0c;并且提示没有可以使用的USB设备&#xff0c;解决方法如下 下载并安装Vitrual Box提供的功能扩展包 分别点击帮助->关于&#xff0c;查看当前使用的版本进入到Virtual Box官网下载链接根…

LabVIEW在金属铜大气腐蚀预测评价系统中的应用

为了应对电子设备和仪器中金属铜因大气腐蚀带来的挑战&#xff0c;开发一种基于LabVIEW平台的先进预测评价系统。这个系统的设计宗旨是准确预测并评估在不同室内外环境中金属铜的腐蚀状况。我们团队在LabVIEW的强大数据处理和图形化编程支持下&#xff0c;结合实际的大气腐蚀数…

【Java语言基础②】Java基本语法——Java程序基本格式,注释,标识符,常量

通过前面的学习&#xff0c;大家对Java语言有了一个基础认识&#xff0c;但现在还无法使用Java语言编写程序&#xff0c;要熟练使用Java语言编写程序&#xff0c;必须充分掌握Java语言的基础知识。今天咱们就来聊一聊Java的基本语法。 1.java程序的基本格式 Java程序代码必须…

制作docker镜像时,使用copy命令统一文件的不同所属用户

一、背景 在制作docker镜像时&#xff0c;使用COPY命令&#xff0c;可以统一原本不同所属用户的文件为同一个用户root。 我们都知道&#xff0c;linux系统&#xff0c;不同的用户之间的访问是受限的。 整个文件夹的用户通体都是devuser&#xff0c;但是里面的文件却是其他用户…

4D 毫米波雷达:智驾普及的新路径(二)

4 4D 毫米波的技术路线探讨 4.1 前端收发模块 MMIC&#xff1a;级联、CMOS、AiP 4.1.1 设计&#xff1a;级联、单芯片、虚拟孔径 4D 毫米波雷达的技术路线主要分为三种&#xff0c;分别是多级联、级联 虚拟孔径成像技术、以及 集成芯片。&#xff08; 1 &#xff09;多级…

训练FastestDet(Anchor-Free、参数量仅0.24M),稍改代码使得符合YOLO数据集排布

文章目录 0 参考链接1 准备数据1.1 使用以下代码生成绝对路径的txt文件1.2 在config文件夹下新建一个xxx.names文件 2 配置训练参数3 稍改代码使得符合YOLO数据集排布4 开始训练 0 参考链接 官方的代码&#xff1a;FastestDet 1 准备数据 我已有的数据集排布&#xff1a;&am…

Python Matplotlib 动画教程:提高可视化吸引力的强大工具【第24篇—python:Matplotlib】

文章目录 &#x1f356; 方法一&#xff1a;使用pause()函数&#x1f680; 方法二&#xff1a;使用FuncAnimation()函数&#x1f94b; 线性图动画&#xff1a;&#x1f3bb; Python中的条形图追赶动画&#x1f30c; Python中的散点图动画&#xff1a;&#x1f6f9; 条形图追赶的…

Date怎么转localDate和localDate转Date

Date怎么转localDate 首先&#xff0c;将java.util.Date对象转换为java.time.Instant对象。Instant是表示时间戳的类&#xff0c;可以精确到纳秒级别。 Date date new Date();Instant instant date.toInstant(); 然后&#xff0c;使用java.time.ZoneId类来指定时区&#xf…

LV.13 D11 Linux驱动移植及内核深化 学习笔记

一、设备树 1.1 设备树 设备树是一种描述硬件信息的数据结构&#xff0c;Linux内核运行时可以通过设备树将硬件信息直接传递给Linux内核&#xff0c;而不再需要在Linux内核中包含大量的冗余编码 举例&#xff1a;让LED2闪烁的代码中&#xff0c;有逻辑代码和设备代码。Li…

VS中动态库的创建和调用

VS中动态库的创建和调用 库 ​ 库是写好的现有的&#xff0c;成熟的&#xff0c;可以复用的代码。库的存在形式本质上来说库是一种可执行代码的二进制。 ​ 库有两种&#xff1a;静态库&#xff08;.a、.lib&#xff09;和动态库&#xff08;.so、.dll&#xff09;。所谓静态…

基于Java SSM框架实现体育竞赛成绩管理系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架实现体育竞赛成绩管理系统演示 摘要 体育竞赛是各种体育体育项目比赛的总称。是在裁判员的主持下&#xff0c;按统一的规则要求&#xff0c;组织与实施的体育员个体或体育队之间的竞技较量&#xff0c;是竞技体育与社会发生关联&#xff0c;并作用于社会的媒…

Java网络爬虫--HttpClient

目录标题 技术介绍有什么优点&#xff1f;怎么在项目中引入&#xff1f; 请求URLEntityUtils 类GET请求带参数的GET请求POST请求 总结 技术介绍 HttpClient 是 Apache Jakarta Common 下的子项目&#xff0c;用来提供高效的、功能丰富的、支持 HTTP 协议的客户端编程工具包。相…

2023年全国职业院校技能大赛软件测试赛题—单元测试卷⑧

单元测试 一、任务要求 题目1&#xff1a;根据下列流程图编写程序实现相应处理&#xff0c;执行j10*x-y返回文字“j1&#xff1a;”和计算值&#xff0c;执行j(x-y)*(10⁵%7)返回文字“j2&#xff1a;”和计算值&#xff0c;执行jy*log(x10)返回文字“j3&#xff1a;”和计算值…