【数据结构】堆的实现以及建堆算法和堆排序

news2024/11/23 19:52:55

【数据结构】堆的实现以及建堆算法和堆排序

🔥个人主页大白的编程日记

🔥专栏数据结构


文章目录

  • 【数据结构】堆的实现以及建堆算法和堆排序
    • 前言
    • 一.堆的实现
      • 1.1 堆数据的插入
      • 1.2堆数据的删除
    • 二.建堆算法和堆排序
      • 2.1思路分析
      • 2.2向上建堆算法
      • 2.3向下调整建堆
      • 2.4堆排序
    • 后言

前言

哈喽,各位小伙伴大家好!上期给大家讲了树,二叉树以及堆。今天带着大家实现堆这个数据结构,以及堆排序。话不多说,咱们进入正题!向大厂冲锋!

一.堆的实现

  • 堆的定义
    我们用数组控制堆,在物理上是一个数组。逻辑上想象成堆。然后用size记录堆的节点个数。后面涉及扩容,所以用capacity记录数组空间大小。这里我们实现的是小堆
typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}HP;
  • 堆的初始化

初始化时我们可以先给堆开好空间,也可以不开。我们不开就先给NULL,然后size和capacity都先给0。

void HPInit(HP* php)
{
	assert(php);
	php->a = NULL;
	php->size = php->capacity = 0;
}//初始化
  • 堆的销毁

我们先free销毁数组,在把size和capacity置为0.

void HPDestroy(HP* php)
{
	assert(php);
	free(php->a);//销毁数组
	php->a = NULL;
	php->capacity = php->size = 0;
}//销毁

1.1 堆数据的插入

  • 思路分析

想要实现堆的插入,我们需要在数组插入数据后进行向上调整。

  • 堆数据插入
    插入数据前我们需要检查一下是否需要扩容。
    第一次没开空间,我们就给4个数据的空间。
    否则我们就realloc扩容为2倍。
    最后在赋值。然后size++更新节点个数。
    再用向上调整。
void HPPush(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(HPDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail~");
			return;
		}
		php->a = tmp;
		php->capacity = newcapacity;
	}
	php->a[php->size++] = x;//插入
	AdjustUp(php->a, php->size);//向上调整
}
  • 向上调整算法

我们size是数据个数,所以插入的child节点下标为size-1.那我们要找到他的父亲节点就是(child - 1) / 2。
然后判断插入数据和父亲节点的大小关系是否满足堆。
不满足就交换父子节点。然后更新child节点的下标。
满足则停止。或直到child节点到根节点时停止。

void AdjustUp(HPDataType* a, int size)
{
	int child = size - 1;//最后的节点
	while (child > 0)
	{
		int parent = (child - 1) / 2;//父亲节点
		if (a[child] < a[parent])//判断
		{
			Swap(&a[child], &a[parent]);//交换
			child = parent;
		}
		else
		{
			break;//调整完成
		}
	}
}

1.2堆数据的删除

  • 思路分析

所以我们需要把最后一个节点和堆的根节点交换后,删除最后一个节点,然后向下调整。

  • 堆数据的删除
    我们先Swap交换根节点和最后一个节点,然后size–删除最后一个节点。再进行向下调整。
void HPPop(HP* php)//删除根节点
{
	assert(php);
	assert(php->size );//判断是否为空
	Swap(&php->a[0], &php->a[php->size-1]);//交换根节点和最后一个节点
	php->size--;//删除堆最后一个数据
	AdjustDown(php->a,php->size,0);//向下调整
}
  • 向下调整算法
    我们先算parent的左孩子child的下标。
    然后用假设法找出左右孩子最大或最小的那个孩子,注意有可能只有左孩子没有右孩子。所以我们用min+1<size判断是否存在右孩子。然后再将找出的最大或最小孩子与parent节点进行比较。如果不满足堆的大小关系就交换,然后更新parent的下标和新的child的下标。
    满足就break停止循环,或当child<=size说明此时parent为叶子节点停止循环。
void AdjustDown(HPDataType* a, int size,int parent)
{
	int child = parent * 2 + 1;//孩子节点
	while (child<size)
	{
		int min = child;//左右孩子中最小的孩子
		if (min+1<size&&a[min] > a[min + 1])//防止没有右孩子
		{
			min = child + 1;
		}//假设法
		if (a[parent] > a[min])//判断
		{
			Swap(&a[parent], &a[min]);//交换
			parent = min;
			child = parent * 2 + 1;
		}
		else
		{
			break;//调整完毕
		}
	}
}
  • 堆的判空
    直接判断是否size为0
bool HPEmpyt(HP* php)
{
	assert(php);
	return php->size == 0;//判空
}
  • 堆的数据个数

直接返回size即可

int HPSize(HP* php)
{
	assert(php);
	return php->size;//返回数据个数
}
  • 堆顶元素

直接返回下标为0的位置数据即可。

HPDataType HPTop(HP* php)
{
	assert(php);
	assert(php->size);//判断是否为空
	return php->a[0];//返回根节点
}

二.建堆算法和堆排序

现在我们要实现堆排序,那我们需要建堆。
那升序建大堆还是小堆,降序建大堆还是小堆呢?还是都可以呢?
升序建大堆 降序建小堆 为什么呢?

2.1思路分析

那如何建堆呢?
有两种建堆算法。

2.2向上建堆算法

我们堆插入的过程其实就是建堆的过程。

for (int i = 1; i < n; i++)
{
	AdjustUp(p,i);
}

2.3向下调整建堆

我们从最后一个父亲节点依次往后向下调整

for (int i = (n - 1 - 1) / 2; i >= 0; i--)
{
	AdjustDown(p, n, i);
}

2.4堆排序

实现堆排序,我们升序建大堆,降序建小堆。
两种建堆算法都可以。
然后我们用end表示最后一个堆元素
每次循环交换堆顶元素和最后一个堆元素。
然后堆顶元素向下调整。更新最后一个堆元素即可。

void HeapSort(HPDataType* p, int n)
{
	for (int i = 1; i < n; i++)//向上建堆
	{
		AdjustUp(p,i);
	}
	int end = n - 1;//最后一个堆元素
	while (end > 0)//
	{
		Swap(&p[0], &p[end]);//交换堆顶元素和最后一个堆元素
		AdjustDown(p,end,0);//向下调整
		end--;//更新最后一个堆元素
	}
}
  • 验证
void HeapSort(HPDataType* p, int n)
{
	for (int i = 1; i < n; i++)//向上建堆
	{
		AdjustUp(p,i);
	}
	int end = n - 1;//最后一个堆元素
	while (end > 0)//
	{
		Swap(&p[0], &p[end]);//交换堆顶元素和最后一个堆元素
		AdjustDown(p,end,0);//向下调整
		end--;//更新最后一个堆元素
	}
}
void test2()
{
	int a[] = { 9,6,4,7,10,5,3,1,2,8};
	HeapSort(a, sizeof(a) / sizeof(a[0]));
	for (int i = 0; i < sizeof(a)/sizeof(a[0]); i++)
	{
		printf("%d ", a[i]);
	}
}
int main()
{
	test2();
	return 0;
}

后言

这就堆的实现以及建堆算法和堆排序。这都是数据结构的重中之重。
大家一定要多加掌握。今天就分享到这,感谢各位大佬的耐心垂阅!咱们下期见!拜拜~

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

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

相关文章

Linux网络编程之UDP

文章目录 Linux网络编程之UDP1、端口号2、端口号和进程ID的区别3、重新认识网络通讯过程4、UDP协议的简单认识5、网络字节序6、socket编程接口6.1、socket常见接口6.2、sockaddr通用地址结构 7、简单的UDP网络程序7.1、服务器响应程序7.2、服务器执行命令行7.3、服务器英语单词…

R语言画散点图-饼图-折线图-柱状图-箱线图-直方图-曲线图-热力图-雷达图

R语言画散点图-饼图-折线图-柱状图-箱线图-直方图-曲线图-热力图-雷达图 散点图示例解析效果 饼图示例解析效果 折线图示例解析效果 柱状图示例解析效果 箱线图示例解析效果 直方图示例解析效果 曲线图使用 curve() 函数示例效果 使用 plot() 函数示例效果 使用 ggplot2 包绘制…

iMazing 3 换手机后苹果游戏数据还有吗 换iPhone怎么转移游戏数据

当你想要更换手机&#xff0c;无论是选择升级到最新款iPhone&#xff0c;或者换到“经典”旧款iPhone&#xff0c;单机游戏数据的转移总是让人发愁。本文将详细介绍换手机后苹果游戏数据还有吗&#xff0c;以及换iPhone怎么转移游戏数据&#xff0c;确保你能无缝继续你的游戏体…

jenkins+gitlab+harbor+maven自动化容器部署

一、gitlab安装配置 1.1、安装 由于比较懒啊&#xff01;这里就直接使用docker安装了啊&#xff01; 没事先更新一个yum源&#xff1a;yum update -y 整一个gitlab镜像&#xff1a;docker pull gitlab/gitlab-ce 运行一个gitlab容器&#xff1a;docker run -d -p 8443:443 -p…

安装 Maven

安装 Maven 的步骤&#xff1a; 1. 访问 Maven 官方网站: https://maven.apache.org/download.cgi 2. 下载 Maven 的二进制文件 3. 解压下载的文件到希望安装的目录 4. 将 Maven 的 bin 目录添加到您的系统环境变量 PATH 中&#xff08;配置环境变量&#xff09; 这个步骤可…

Ubuntu24.04 NFS 服务配置

1、NFS 介绍 NFS 是 Network FileSystem 的缩写&#xff0c;顾名思义就是网络文件存储系统&#xff0c;它允许网络中的计算机之间通过 TCP/IP 网络共享资源。通过 NFS&#xff0c;我们本地 NFS 的客户端应用可以透明地读写位于服务端 NFS 服务器上的文件&#xff0c;就像访问本…

el-menu弹出菜单样式不生效

1. 使用 ruoyi 项目时出现的问题。 <template><el-menu:default-active"activeMenu":collapse"false":unique-opened"true"class"container":collapse-transition"true"mode"horizontal"><sideba…

CH01_WPF概述

第1章&#xff1a;WPF概述 本章目标 了解Windows图形演化了解WPF高级API了解分辨率无关性概念了解WPF体系结构了解WPF 4.5 WPF概述 ​ 欢迎使用 Windows Presentation Foundation (WPF) 桌面指南&#xff0c;这是一个与分辨率无关的 UI 框架&#xff0c;使用基于矢量的呈现引…

核函数支持向量机(Kernel SVM)

核函数支持向量机&#xff08;Kernel SVM&#xff09;是一种非常强大的分类器&#xff0c;能够在非线性数据集上实现良好的分类效果。以下是关于核函数支持向量机的详细数学模型理论知识推导、实施步骤与参数解读&#xff0c;以及两个多维数据实例&#xff08;一个未优化模型&a…

【iOS】—— isMemberOfClass isKindOfClass以及源码

【iOS】—— isMemberOfClass & isKindOfClass以及源码 isa指针示例源码解析&#xff1a;isKindOfClass&#xff1a;源码解析&#xff08;实例方法和类方法&#xff09;isMemberOfClass&#xff1a;源码解析&#xff08;实例方法和类方法&#xff09;源码分析总结&#xff…

MF173:将多个工作表转换成PDF文件

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

【C++】16. set 和 map

在之前的博客中&#xff0c;我们已经接触过STL中的部分容器&#xff0c;比如&#xff1a;vector、list、deque等&#xff0c;这些容器统称为序列式容器&#xff0c;因为其底层为线性序列的数据结构&#xff0c;里面存储的是元素本身。 我们这篇博客的内容是关联式容器&#xff…

负重3Kg垂起固定翼无人机技术详解

一、基本参数 负重3Kg垂起固定翼无人机是一款具备强大负载能力和长航时特性的无人机。其基本参数如下&#xff1a; - 最大负载&#xff1a;3Kg - 最大续航时间&#xff1a;203分钟&#xff08;示例数据&#xff0c;实际续航时间可能根据任务负载、环境条件等因素有所不同&…

敲详细的springframework-amqp-rabbit源码解析

看源码时将RabbitMQ的springframework-amqp-rabbit和spring-rabbit的一套区分开&#xff0c;springboot是基于RabbitMQ的Java客户端建立了简便易用的框架。 springboot的框架下相对更多地使用消费者Consumer和监听器Listener的概念&#xff0c;这两个概念不注意区分容易混淆。…

【electron6】浏览器实时播放PCM数据

pcm介绍&#xff1a;PCM&#xff08;Puls Code Modulation&#xff09;全称脉码调制录音&#xff0c;PCM录音就是将声音的模拟信号表示成0,1标识的数字信号&#xff0c;未经任何编码和压缩处理&#xff0c;所以可以认为PCM是未经压缩的音频原始格式。PCM格式文件中不包含头部信…

FPGA文档阅读

FPGA的文档没有相应的基础还真不容易看懂&#xff0c;下面是B站上对FPGA文档的解读(本文非对文档解读&#xff0c;只是为个人记录第三期&#xff1a;CycloneIV E最小系统板设计&#xff08;一&#xff09;从Datasheet上获取FPGA的基本参数_哔哩哔哩_bilibili 电源部份 核心电…

elasticsearch, kibana, 6.8.18 版本下的创建索引,指定timestamp,java CRUD,maven版本等

ELK 这一套的版本更迭很快&#xff0c; 而且es常有不兼容的东西出现&#xff0c; 经常是搜一篇文章&#xff0c;看似能用&#xff0c;拿到我这边就不能用了。 很是烦恼。 我这边的ELK版本目前是 6.8.18&#xff0c;这次的操作记录一下。 &#xff08;涉密内容略有删改&#xf…

SQL语句——DDL数据定义语言

1.sql语言不区分大小写 2._&#xff08;下划线&#xff09;进行名字的分割&#xff0c;不适用驼峰命名 3.; 语句sql结尾处加一个;来表示结束 4.一般关键词建议用大写 5.所有名称不能用中文 1.创建数据库 CREATE DATABASE [IF NOT EXISTS] 库名 -- 库 #创建库 #create databa…

计算机网络序章

计算机网络学习什么&#xff1f; 下列举例由用户使用计算机角度去理解 首先&#xff0c;计算机网络是通过路由等方式去获取我们希望的数据用户可以在APP中去进行方便的操作去获取数据。每个应用都有自己的端口去确定每次来的数据是否是自己需要的数据应该应该传到哪里&#x…

COD论文笔记 Deep Gradient Learning for Efficient Camouflaged 2022

动机 这篇论文的动机在于解决伪装目标检测(COD)中的一个关键问题&#xff1a;在复杂背景下&#xff0c;伪装目标与背景的边界模糊&#xff0c;使得检测变得极其困难。现有的方法&#xff0c;如基于边界或不确定性的模型&#xff0c;通常仅响应于伪装目标的稀疏边缘&#xff0c…