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

news2024/11/26 0:47:45

  • 1.堆的概念
  • 2.堆的实现 (建小堆为例)
    • 2.1 初始化和销毁
    • 2.2 判空
    • 2.3 获得堆顶元素和堆的大小
    • 2.4 插入
    • 2.5 删除
  • 3.堆的构建(建小堆为例)

1.堆的概念

将若干数据或元素按照完全二叉树的存储方式顺序存储到一个一维数组中,在逻辑结构中(完全二叉树)满足每个根结点的值不大于或不小于它的左右孩子结点的值。满足上述条件称为小堆或大堆。

堆的性质:堆中某节点的值不大于不小于双亲结点的值;堆在逻辑结构上是一棵完全二叉树。

比如下图是一个小堆 (数据的增删都以这个数组为例)
在这里插入图片描述

堆只需要满足双亲结点的值不大于或不小于孩子结点的值即可,左右孩子结点的值不做要求。比如上图的13和17互换位置依然是小堆。

注意,如果元素在数组中有序,则一定可以构成堆,反之不成立。

2.堆的实现 (建小堆为例)

由于堆的物理结构使用的是数组,因此定义的结构体和顺序表类似

typedef int heapDataType;

typedef struct heapNode
{
	heapDataType* array;
	int size;
	int capacity;
}heapNode;

关于堆的接口,包括初始化和销毁,插入,删除,判空,以及获取堆顶元素和堆的大小。

2.1 初始化和销毁

和顺序表非常类似,就不赘述了,重点放在插入数据和删除数据。

//初始化堆
void heapInit(heapNode* hp)
{
	assert(hp);
	hp->array = NULL;
	hp->capacity = hp->size = 0;
}

//销毁堆
void heapDestroy(heapNode* hp)
{
	assert(hp);
	hp->array = NULL;
	hp->capacity = hp->size = 0;
}

2.2 判空

由于结构体内有成员变量size,直接返回size是否等于0即可

//判空
bool heapIsEmpty(heapNode* hp)
{
	assert(hp);
	return hp->size == 0;
}

2.3 获得堆顶元素和堆的大小

根就是堆顶,在物理结构上,堆顶就是数组的首元素。因此返回数组首元素即可。

//获得堆顶数据
heapDataType heapTop(heapNode* hp)
{
	assert(hp);
	//堆不能为空
	assert(!heapIsEmpty(hp));
	return hp->array[0];
}

//获得堆的大小
int heapSize(heapNode* hp)
{
	assert(hp);
	return hp->size;
}

2.4 插入

增加数据的时候,增加在数组的最后一个位置,增加数据前为堆,增加数据后也需要还是堆。
但数据的不同,可能影响结构。
在这里插入图片描述
在数组末尾增加一个大于或等于17的值时,堆的结构不会被破坏,如果增加的数据小于17,则需要将其调整为堆。

调整需要用到向上调整算法。指的是将符合条件的数据进行上移。
调整过程为,和该结点的双亲结点的值进行比较,如果小于双亲结点的值,那么进行交换,再作为新的孩子和新的双亲结点的值比对,直到换到根节点为止。

以插入一个7为例,调整过程如下图所示。
当调整的结点处于根节点时,则不需要进行下去了,即child下标为0
在这里插入图片描述
调整过程中可能涉及到的结点为从根节点到插入的新结点上的一条路径上的结点。
我们知道,在完全二叉树中,任一结点(设下标为k)的双亲结点的下标为(k-1)/ 2

代码实现如下

//交换
void swap(heapDataType* x1, heapDataType* x2)
{
	heapDataType tmp = *x1;
	*x1 = *x2;
	*x2 = tmp;
}

//向上调整算法
void AdJustUp(heapDataType* array,int child)
{
    //双亲节点的下标
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (array[child] < array[parent])
		{
			//交换数据
			swap(&array[child], &array[parent]);
			//更新下标
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

//插入
void heapPush(heapNode* hp, heapDataType x)
{
	assert(hp);
	//容量不够,扩容
	if (hp->size == hp->capacity)
	{
		int newCapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
		//空间可能开辟失败,为保留原数据,创建新的指针变量
		heapDataType* tmp = realloc(hp->array, sizeof(heapNode) * newCapacity);
		if (tmp == NULL)
		{
			return;
		}
		//开辟成功
		hp->capacity = newCapacity;
		hp->array = tmp;
	}
	//插入数据
	hp->array[hp->size++] = x;
	//向上调整重新建堆
	AdJustUp(hp->array,hp->size-1);
}

插入数据和顺序表中完全一样,只是多了一步向上调整。

2.5 删除

堆的删除指的是删除堆顶的数据。对于数组,我们比较容易想到的是数据的挪动覆盖,但是这样会导致一个问题,就是删除数据后,堆结构被破坏的很严重。使得难以将其恢复成堆。因此这种做法不合适。

这里的做法是,将堆顶数据和最后一个数据进行交换,然后在删除。这样做虽然也会破坏堆的结构,但是破坏的程度没有那么大,可以通过算法(向下调整算法)将其恢复成堆。

刚刚我们插入了一个7,现在我们在删除一次堆顶的数据。

首先,交换首尾数据并删除,从根节点开始向下调整,找到左右孩子结点中值小的那个进行交换,在作为新的双亲结点,找新的值较小的孩子结点进行交换,直到换到叶子结点。

举例删除7,删除过程如图所示。
在这里插入图片描述
如果25处数据是15,则在进行一次交换即可。
有几个小问题需要处理,1.怎么找到较小的孩子结点。2.调整的结束条件。

对于问题1:可以使用假设法,假设左孩子值较小,在和右孩子的值进行比较,如果存在右孩子且右孩子值小,则下标加一
对于问题2:在数组中,孩子结点的下标不会超过数组大小。如果孩子结点的下标已经超过数组,说明调整的结点已经是叶子结点,则不要在进行调整了。

代码如下

//向下调整
void AdJustDown(heapDataType* array, int num_array, int parent)
{
	//孩子下标
	int child = parent * 2 + 1;
	
	while (child < num_array)
	{
	    //找到值小的孩子,if条件1指存在右孩子,条件2指右孩子值小
		if (child + 1 < num_array && array[child + 1] < array[child])
		{
			++child;
		}
		if (array[child] < array[parent])
		{
			//交换数据
			swap(&array[child], &array[parent]);
			//更新下标
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

//删除
void heapPop(heapNode* hp)
{
	assert(hp);
	//堆不能为空
	assert(!heapIsEmpty(hp));

	//交换堆的首尾数据
	swap(&hp->array[0], &hp->array[hp->size - 1]);
	hp->size--;
	//向下调整重新建堆
	AdJustDown(hp->array, hp->size, 0);
}

3.堆的构建(建小堆为例)

随机给一个数组,将这个数组构建成为堆。
这里有两种方法,第一种是依次插入数据,向上调整建堆。一个数据可以看作堆,依次插入数据后,堆结构被破坏,进行向上调整,数组遍历完全后,就构建成了堆。

int main()
{
	srand(time(NULL));
	heapNode hp;
	heapInit(&hp);

	//生成十个随机数,插入堆
	for (int i = 0; i < 10; i++)
	{
		int j = rand() % 100;
		heapPush(&hp, j);
	}

	//打印,数据符合小堆
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", hp.array[i]);
	}
	printf("\n\n");
	//该操作为获取堆顶数据,打印后,在删除,最终结果为升序
	while (!heapIsEmpty(&hp))
	{
        printf("%d ", heapTop(&hp));
		heapPop(&hp);
	}
	heapDestroy(&hp);
	return 0;
}

打印结果如图
在这里插入图片描述

第二种方法为向下调整建堆。
向下调整算法有一个前提,指的是调整的结点的左右子树需要是堆。因此从根节点开始调整的方法不适用了,一个值可以看成是堆,因此从倒数的第一个非叶子结点开始向下调整。
在这里插入图片描述
将该数组打乱成25,36,10,13,17,42

向下调整建堆的过程如下图所示
在这里插入图片描述

那么第一个非叶子结点下标是多少呢?
首先,最后一个非叶子结点是最后一个结点的双亲结点,下标需要结合数组和完全二叉树的性质来计算。假设数组中元素个数为n,则最后一个元素下标为n-1,完全二叉树中,下标为n-1的双亲结点的下标为[ ( n - 1 ) -1 ] / 2 即 ( n - 2)/ 2

调整后数组就符合堆结构了。

int main()
{
	int arr[10] = { 0 };
	//随机生成二十个树
	for (int i = 0; i < 10; i++)
	{
		arr[i] = rand() % 100 + 10;
	}
	
	//打印随机数
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	//向下调整建堆
	for (int j = (10 - 2) / 2; j >= 0; --j)
	{
		AdJustDown(arr, 10, j);
	}
	printf("\n\n");

	//打印调整后的数组
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
}

打印结果如图
在这里插入图片描述

堆的介绍就先到这里了。
本篇主要介绍二叉树的顺序存储,也就是堆了,关于堆的应用(堆排序,topk问题)就留到下次在介绍吧。

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

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

相关文章

jupyter加目录contrib nbextension 使用

jupyter在工作中使用很方便&#xff0c;可是一旦单个文件的内容多了&#xff0c;即使有markdown文本提醒&#xff0c;上下翻找依旧很费精力。这时&#xff0c;有个目录就显得至关重要了。 1 安装模块 打开anaconda的PowerShell&#xff08;带有Prompt的&#xff09;&#xff0…

Springboot中的三层架构

我们在进行前后端交互的时候&#xff0c;会分为数据访问&#xff0c;业务逻辑&#xff0c;接受请求并响应数据三个操作&#xff0c;这三部分其实是可以拆分的&#xff0c;让他们解耦&#xff0c;否则代码复用性差并且不易维护&#xff0c;所以诞生了三层架构——1.Dao(数据访问…

js使用canvas实现画roi功能,并实现交集并集差集操作,附源码

效果概览 支持圆形,矩形,旋转矩形绘制,鼠标像素拾取,图片缩放,图片拖拽,像素测量,roi交集并集补集输出 TODO:实现自由路径绘制,与后台交互数据 实现原理 交集并集差集使用像素做运算,使用0代表没有像素,1代表有像素,然后再做运算 // 计算交集calculateIntersec…

Mouse on Mouse(M.O.M.™)Immunodectection Kits

在检测小鼠组织抗原的实验中&#xff0c;我们经常会遇到所找到的一抗只有小鼠来源的情况&#xff0c;也就经常会存在着多种疑虑&#xff1a;比如抗小鼠的二抗不仅识别小鼠源性的一抗&#xff0c;同时也识别小鼠组织中内源性的小鼠免疫球蛋白&#xff0c;因而背景非常高。 而实际…

2024年通信安全员ABC证证考试题库及通信安全员ABC证试题解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年通信安全员ABC证证考试题库及通信安全员ABC证试题解析是安全生产模拟考试一点通结合&#xff08;安监局&#xff09;特种作业人员操作证考试大纲和&#xff08;质检局&#xff09;特种设备作业人员上岗证考试大…

c++的STL(6)-- map和multimap

map和multimap概述 map和multimap中存储的是使用pair对象的键值对。 map和multimap底层也是使用红黑树的数据结构进行实现的。所以&#xff0c;map和multimap内部存储的键值对也是有序的。并且内部数据也是使用链表的形式进行关联。所以其的迭代器和指针&#xff0c;也只能进行…

Gmail多账号防封攻略,看这一篇就够了

您的业务活动需要多个 Gmail 帐户吗&#xff1f;出海畅游&#xff0c;Gmail账号是少不了的工具之一&#xff0c;可以关联到Twitter、Facebook、Youtube、Chatgpt等等平台&#xff0c;可以说是海外网络的“万能锁”。但是大家都知道&#xff0c;以上这些平台注册多账号如果产生关…

初识UART串行总线,51单片机串口通信

串口基本认知 串行接口简称串口&#xff0c;也称串行通信接口或串行通讯接口&#xff08;通常指COM接口&#xff09;&#xff0c;是采用串行通信方式的扩展接口。串行接口&#xff08;Serial Interface&#xff09;是指数据一位一位地顺序传送。其特点是通信线路简单&#xff0…

素组主元素

主要元素&#xff08;主元素&#xff09;是在一整数序列&#xff08;长度是N&#xff09;中出现次数大于N/2的元素&#xff0c;因为整数序列本身长度就是N&#xff0c;那么只要该整数序列中存在主元素&#xff0c;主要元素就是唯一的 思路&#xff1a; 思路一&#xff1a;穷举…

git可视化工具

Gitkraken GitKraken 是一款专门用于管理和协作Git仓库的图形化界面工具。它拥有友好直观的界面&#xff0c;使得Git的操作变得更加简单易用&#xff0c;尤其适合那些不熟悉Git命令行的开发者。GitKraken提供了丰富的功能&#xff0c;如代码审查、分支管理、仓库克隆、提交、推…

ADB 命令之 模拟按键/输入

ADB 命令之 模拟按键/输入 模拟按键/输入 在 ​​adb shell​​​ 里有个很实用的命令叫 ​​input​​&#xff0c;通过它可以做一些有趣的事情。 ​​input​​ 命令的完整 help 信息如下&#xff1a; Usage: input [<source>] <command> [<arg>...] Th…

《Python之路:系统自学指南》

引言 在当今信息时代&#xff0c;编程已经成为一项越来越重要的技能。而Python作为一门功能强大、易学易用的编程语言&#xff0c;受到了越来越多人的青睐。然而&#xff0c;学习Python并不是一蹴而就的事情&#xff0c;尤其是对于没有编程基础的初学者来说&#xff0c;往往需…

vscode上编辑vba

安装xvba插件更换vscode的工作目录启动扩展服务器在config.json中添加目标工作簿的名称加载excel文件&#xff08;必须带宏的xlsm&#xff09;这个扩展就会自动提取出Excel文件中的代码Export VBA&#xff08;编辑完成的VBA代码保存到 Excel文件 &#xff09;再打开excel文件可…

mongodb的安装与配置

文章目录 前言一、下载下载mongodb主体下载mongodb shell下载mongodb compass(GUI) 二、安装三、配置四、添加环境变量五、mongodb&#xff0c;启动&#xff01;&#xff01;&#xff01; 前言 开始学习mongodb了&#xff0c;学习资料是黑马程序员的视频 https://space.bilibi…

从PDF到高清图片:一步步学习如何转换PDF文件为高清图片

引言 PDF文件是一种便携式文档格式&#xff08;Portable Document Format&#xff09;&#xff0c;最初由Adobe Systems开发&#xff0c;用于在不同操作系统和软件之间保持文档格式的一致性。PDF文件通常包含文本、图片、图形等多种元素&#xff0c;并且可以以高度压缩的方式存…

LangChain Demo | 如何调用stackoverflow并结合ReAct回答代码相关问题

背景 楼主决定提升与LLM交互的质量&#xff0c;之前是直接prompt->answer的范式&#xff0c;现在我希望能用上ReAct策略和能够检索StackOverflow&#xff0c;让同一款LLM发挥出更大的作用。 难点 1. 怎样调用StackOverflow step1 pip install stackspi step 2 from la…

制造型企业实施WMS仓储管理系统前后的变化

在科技浪潮的推动下&#xff0c;WMS仓储管理系统逐渐崭露头角&#xff0c;成为制造企业优化运营、提升竞争力的得力助手。本文将从制造企业实施WMS仓储管理系统前后的对比入手&#xff0c;探讨这一变革所带来的深远影响。 一、WMS仓储管理系统实施前的仓储管理挑战 在WMS仓储管…

Games101-作业5-光线与三角形相交

Games101-作业5-光线与三角形相交 分析 这次作业还是比较简单的&#xff0c;主要是光线投射&#xff0c;需要填的部分就是把相机发出的光线坐标变换到物体所在的世界坐标和利用上课讲过的公式计算射线与三角形的交点。 想搞清楚这个过程可以参考Ray-Tracing: Generating Came…

Linux:入门篇

文章目录 前言1. Linuxd的安装环境2.Linux的简单介绍2.1 新建目录2.2 新建文件 3.指令到底是什么&#xff1f;4.shell命令以及运行原理5.总结 前言 很多人对于Linux的学习总是感觉无法下手&#xff0c;不知道从何开始学习&#xff0c;相信这篇文章将会为你提供一个清晰的思路。…