数据结构-堆的创建(详解)

news2024/11/28 6:36:27

目录

堆的概念及结构

 堆的实现

堆的向上调整算法

代码实现:

思路详解:

 堆的向下调整算法

代码实现:

思路详解:

堆的创建:

堆结构的定义及相关函数的声明:

堆的初始化:

堆的销毁:

堆的插入:

堆的删除:

取得堆顶元素:

堆的判空:

堆中元素的个数:

向上调整算法:

向下调整算法:

交换: 


堆的概念及结构

堆:1 完全二叉树

任何一个数组都可以看成一个完全二叉树,所以堆的物理结构就是数组,逻辑结构是一颗完全二叉树

       2 大堆:树中任何一个父亲都大于或者等于孩子

          小堆:树中任何一个父亲都小于或者等于孩子

3 父子下标关系 leftchild = parent*2+1   rightchild = parent*2+2

                         parent = (child-1)/2

 堆的实现

堆的向上调整算法

代码实现:

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

//向上调整算法--建小堆
void AdjustUp(int* 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;
		}
		
	}
}

思路详解:

向上调整建堆其实就是一个插入的过程,向上调整算法的前提是插入之前,已经是堆

下面我以建小堆的思路来介绍:

 每插入一个元素,可能需要多次向上调整,即可能会与自己所有的祖先节点比较,首先和其父亲 (下标是:(child-1)/2 )比较,若是小于父亲则二者交换,此元素作为孩子向上调到父亲的位置,然后再和这个位置上的父亲比较,过程一样……,若是大于父亲则无需向上调堆,位置合适

孩子节点何时停止向上调整 ?

 1 孩子节点处在合适的位置 即孩子节点大于父亲节点

or:孩子节点调整至堆顶,调无可调

 插入的孩子60比父亲56大,位置合适,插入后还是小堆,无需向上调整

插入的孩子50小于其父亲56,需要向上调整:

孩子50和父亲56交换位置,现在孩子50处在父亲56的位置,然后求出孩子50现在的下标,即child=parent,继而求出孩子50现在的父亲下标 parent = (child-1)/2 

孩子50小于现在的父亲10,位置合适,结构保持为堆,无需继续向上调整

 

孩子8比父亲56小,向上调整,调到父亲56的位置,继续与现在位置上的父亲10比较,小于,向上调整, 调到父亲10的位置,此时已经处在堆顶,下标为0,无需继续调堆

 堆的向下调整算法

代码实现:

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

//向下调整算法--建小堆
void AdjustDown(int* a, int n,int parent)
{
	int child = parent * 2 + 1;//假设左右孩子中最小的孩子是左孩子
	while (child < n)//只要一个父亲节点还有左孩子,则有继续向下调整的可能
	{
		if (child + 1 < n && a[child + 1] < a[child])//右孩子存在且右孩子<左孩子,假设不成立,更换有孩子为最小的孩子,child更新为右孩子的下标,右孩子的下标为左孩子的下标+1
		{
			child++;
		}

		if (a[child] < a[parent])//左右孩子中最小的孩子比父亲小,交换二者位置
		{
			Swap(&a[child], &a[parent]);
			parent = child;//父亲节点现在处在最小孩子的位置
			child = parent * 2 + 1;//父亲节点现在最小孩子(依然先做假设)
		}
		else//左右孩子中最小的孩子都比父亲节点大,父亲节点无需向下调整,位置合适,保持小堆
		{
			break;
		}
	}
}

思路详解:

向下调整算法有一个前提:左右子树必须是一个堆,才能调整

1 从父节点开始向下调整

  先选出左右孩子中最小/最大的一个,然后与其比较:下面我按小堆的形式介绍

  若是父亲小于孩子中最小的一个,则无需向下调整

  若是父亲大于孩子中最小的一个,则二者交换,父亲调至最小孩子的位置

  再求出它现在最小孩子的下标,二者比较,重复上述过程

2 父节点何时停止向下调整:

  父节点处在合适的位置,父节点小于最小的孩子

or:父节点调至叶子节点,调无可调,此时的父节点是没有左孩子的,故而当父亲的要与之交换的孩子下标越界后,就无需再向下调整了

                                          

 

 

 

 

 

堆的创建:

堆结构的定义及相关函数的声明:

堆是一颗完全二叉树,任何一个数组都可以看成一颗完全二叉树,所以堆结构就可以用数组来实现

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int HPDataType;

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

void HeapInit(HP* php);
void HeapDestroy(HP* php);
void HeapPush(HP* php, HPDataType x);
void HeapPop(HP* php);
HPDataType HeapTop(HP* php);
bool HeapEmpty(HP* php);
int HeapSize(HP* php);

void AdjustUp(HPDataType* a, int child);
void AdjustDown(int* a, int n, int parent);

堆的初始化:

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;
}

堆的插入:

void HeapPush(HP* php, HPDataType x)
{
	assert(php);
	if (php->size == php->capacity)//空间满了需要扩容
	{
		int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;
		HPDataType* tmp = (HPDataType*)realloc(php->a,sizeof(int) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		php->a = tmp;
		php->capacity = newcapacity;
	}

	php->a[php->size] = x;
	php->size++;
	AdjustUp(php->a, php->size - 1);//每插入一个元素需要向上调整,以保持堆结构
}

堆的删除:

删除的是堆顶的元素,方法是:

1 将堆顶元素与最后一个元素交换

2 删除最后一个元素,即实现删除堆顶元素的效果

3 从现在的堆顶位置开始向下调整,以维持堆结构不变

void HeapPop(HP* php)
{
	assert(php);
	assert(!HeapEmpty(php));//为空不能删除
	Swap(&php->a[0], &php->a[php->size - 1]);
	php->size--;
	AdjustDown(php->a, php->size, 0);
}

取得堆顶元素:

HPDataType HeapTop(HP* php)
{
	assert(php);
	assert(!HeapEmpty(php));//为空不能取数据
	return php->a[0];
}

堆的判空:

bool HeapEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}

堆中元素的个数:

int HeapSize(HP* php)
{
	assert(php);
	return php->size;
}

向上调整算法:

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;
		}
	}
}

向下调整算法:

void AdjustDown(int* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && a[child + 1] < a[child])
		{
			child++;
		}

		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

交换: 

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

现在我们来使用这个堆结构吧,将数组建成一个小堆,不断打印堆顶元素我们会得到一个递增序列,但注意!!建成小堆后数组任然无序,因为堆只规定了父子间的关系,没有规定兄弟间的关系

得到最小的堆顶元素后,删除堆顶元素,新交换到堆顶的元素向下调整,会得到第二小的元素,然后是第三小,第四小……所以不断打印堆顶元素会是有序序列

int main()
{
	HP heap;
	HeapInit(&heap);
	int arr[] = { 9,8,7,6,5 };
	int sz = sizeof(arr) / sizeof(arr[0]);

	
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		HeapPush(&heap, arr[i]);
	}

	while (!HeapEmpty(&heap))
	{
		int Top = HeapTop(&heap);
		HeapPop(&heap);
		printf("%d ", Top);
	}

	HeapDestroy(&heap);
	return 0;
}

堆中的元素:物理结构如上

可以看到数组成为小堆后仍然不是有序的

逻辑结构图:

不断打印堆顶元素,得到一个递增序列:

 

 完结撒花~

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

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

相关文章

如何减少图片大小内存?压缩图片内存大小的方法介绍

什么时候需要压缩图片内存大小 随着移动设备的普及和社交媒体的盛行&#xff0c;我们经常需要通过分享图片来传达信息和与他人互动。然而&#xff0c;有时候图片的文件大小过大&#xff0c;可能会导致上传、下载或发送过程中遇到问题。以下是一些常见情况下需要压缩图片内存大…

插值应用案例2

案例1 高点和高程 在一丘陵地带测量高程&#xff0c;x和y方向每隔100m测一个点&#xff0c;得到高程如下表所列&#xff0c;试插值一曲面&#xff0c;确定合适的模型&#xff0c;并由此测到最高点和相应的高程。 x0/z0\y0 100 200 300 400 500 100 636 697 624 478 …

Android GlSurfaceView 入门教程 : 绘制一个三角形

1. GlSurfaceView是什么 GlSurfaceView是Android中的一个类&#xff0c;继承自SurfaceView&#xff0c;用于显示OpenGL ES图形渲染的一个视图。 OpenGL ES是一种跨平台的图形API&#xff0c;用于渲染2D和3D图形&#xff0c;也可以将相机的画面显示到GlSurfaceView上&#xff0…

Spring Boot 中的 SQL 注入攻击是什么,原理,如何预防

Spring Boot 中的 SQL 注入攻击是什么&#xff0c;原理&#xff0c;如何预防 随着互联网的发展&#xff0c;Web 应用程序的数量不断增加&#xff0c;而 SQL 注入攻击也成为了常见的网络安全问题之一。SQL 注入攻击是通过在 Web 应用程序中注入恶意的 SQL 代码&#xff0c;从而…

@RestController 和 @Controller的区别?

Controller 返回一个页面单独使用 Controller 不加 ResponseBody的话一般使用在要返回一个视图的情况&#xff0c;这种情况属于比较传统的Spring MVC 的应用&#xff0c;对应于前后端不分离的情况 RestController 返回JSON 或 XML 形式数据 但RestController只返回对象&#…

FPGA综合设计实验:基于PWM脉宽调制的呼吸流水灯设计

目录 一、引言 二、项目准备 1.项目预期目标 2.项目原理及总体实现思路 三、项目模块设计 1.顶层模块 2.按键控制模块 3.呼吸灯模块 4.数码管显示模块 5.二进制转BCD码模块 四、项目测试 1.仿真测试 2.实物测试 五、项目总结 1.选题思考与过程反思 2.设计的具体…

MySQL之常见的CRUD面试题【上】

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于MySQL数据库的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 导读&#xff1a; 一.数据库的连表…

如何获得免费英文文献翻译呢?

我偶然之间发现的一个小技巧分享给大家&#xff01; 这个方法很简单&#xff0c;巧妙地运用了某外国浏览器自带的翻译功能&#xff0c;所以我们需要的是某外国浏览器 1.将需要翻译的pdf文档保存 2.进入http://pdfdo.com/pdf-to-html.aspx将PDF转为网页 3. 上传文件后耐心等待转…

web学习--SpringMVC--1 基础学习

写在前面&#xff1a; 所有的web学习基于springboot项目&#xff0c;而不会去单独的使用spring来进行。 文章目录 SpringMVC介绍原理MVC模式 入门使用导入依赖编写controller类 详细介绍注解详解ControllerRequestMappingResponseBodyRestControllerRequestParamRequestBodyR…

聚观早报 | 字节跳动要造机器人;苹果已开发悬空虚拟键盘

今日要闻&#xff1a;字节跳动要造机器人&#xff1b;苹果已开发悬空虚拟键盘&#xff1b;苹果汽车或售价9万美元&#xff1b;全球首例猪心脏移植患者仅存活60天&#xff1b;首款搭载ChatGPT的自行车问世 字节跳动要造机器人 7 月 3 日消息&#xff0c;「机器人」作为未来科技…

MySQL-分库分表详解(四)

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a; 小刘主页 ♥️努力不一定有回报&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️学习两年总结出的运维经验&#xff0c;以及思科模拟器全套网络实验教程。专栏&#xf…

记录安装ESXI-6.7遇到的问题(DELL)

从官网下载完ISO镜像,使用rufus制作启动盘 安装过程中报错信息 缺少网卡驱动 安装打包网卡驱动请查看此链接:https://www.cnblogs.com/Sunzz/p/11438066.html 通过检测网卡驱动后,报错信息 解决办法 关闭BIOS中的Secure Boot 具体步骤如下&#xff1a; 1.按F2进入bios 2…

最长回文子串 (力扣) 动态规划 JAVA

给你一个字符串 s&#xff0c;找到 s 中最长的回文子串。 如果字符串的反序与原始字符串相同&#xff0c;则该字符串称为回文字符串。 示例 1&#xff1a; 输入&#xff1a;s “babad” 输出&#xff1a;“bab” 解释&#xff1a;“aba” 同样是符合题意的答案。 示例 2&#…

FlinkSQL 解析字符串+行转列

近期遇到一个实时flinksql需求&#xff0c;需要根据ids数组字段解析成名称数组字段。。。 其中parent_path存放的内容是点号分割的字符串"1659077318807721985.1659120595539924993.1659121050219255810" 第一步&#xff1a;新建kafka source源 create TEMPORARY t…

怎样处理服务器无法复制粘贴问题?服务器不能复制粘贴怎么办?

我们经常需要在服务器上进行一些操作&#xff0c;如复制粘贴文件、修改配置等等。但有时候我们会遇到服务器无法使用复制粘贴功能的问题&#xff0c;这时该怎么办呢&#xff1f;以下是一些解决方法。 1&#xff0e;检查RDP剪贴板功能是否开启 在远程桌面连接(RDP)中&#xff…

基于多案例系统学习防洪评价报告编制方法与水流数学模型建模

查看原文>>>基于多案例系统学习防洪评价报告编制方法与水流数学模型建模 随着社会经济的快速发展&#xff0c;我国河道周边土地开发利用率不断增大&#xff0c;临河建筑物与日俱增&#xff0c;部分河道侵占严重&#xff0c;导致防洪压力增大。加之部分河流沿岸临河建…

Win10无法安装到这个磁盘,选中的磁盘具有mbr分区

关闭这个窗口执行一下操作; 1.按住 shiftF10快捷键&#xff0c;打开命令提示符终端&#xff1b; 2.输入diskpart &#xff0c;并按下回车 &#xff1b; 3.输入list disk&#xff0c;并按下回车&#xff0c;查看盘的使用情况 ; 4.输入 select disk 0 &#xff0c;并按下回车&am…

Mybatis-SQL分析组件 | 京东云技术团队

背景 大促备战&#xff0c;最大的隐患项之一就是慢sql&#xff0c;带来的破坏性最大&#xff0c;也是日常工作中经常带来整个应用抖动的最大隐患&#xff0c;而且对sql好坏的评估有一定的技术要求&#xff0c;有一些缺乏经验或者因为不够仔细造成一个坏的sql成功走到了线上&am…

SpringBoot使用EasyExcel实现Excel的导入导出

一、概念 EasyExcel 是一个基于 Java 的、快速、简洁、解决大文件内存溢出的 Excel 处理工具。它能让你在不用考虑性能、内存的等因素的情况下&#xff0c;快速完成 Excel 的读、写等功能。 二、EasyExcel常用注解 注解名称属性介绍ExcelProperty value属性设置表头的名称 i…