堆的实现以及应用

news2025/1/13 15:34:46

💓博主个人主页:不是笨小孩👀
⏩专栏分类:数据结构与算法👀 刷题专栏👀 C语言👀
🚚代码仓库:笨小孩的代码库👀
⏩社区:不是笨小孩👀
🌹欢迎大家三连关注,一起学习,一起进步!!💓

在这里插入图片描述

  • 堆的实现
    • 堆的结构
    • 堆的接口及实现
      • 堆的插入
      • 堆的删除
      • 其他接口
  • 堆的应用
    • 堆排序
      • 向上调整法建堆
      • 向下调整法建堆
    • TopK问题

堆的实现

我们说堆在物理上是一个数组,逻辑上它是一个完全二叉树,我们可以通过它的下标来计算父亲和孩子之间的关系。
> 左孩子=父亲×2+1;
右孩子=父亲×2+2;
父亲=(孩子-1)/2;

堆的结构

堆的结构和顺序表是一样的。

typedef int HPDateType;

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

堆的接口及实现

堆的接口有哪些呢?

//初始化
void HeapInit(HP* php); 

//销毁
void HeapDestroy(HP* php);

//插入
void HeapPush(HP* php, HPDateType x);

//删除
void HeapPop(HP* php);

//取对顶的数据
HPDateType HeapTop(HP* hp);

// 堆的数据个数
int HeapSize(HP* hp);

// 堆的判空
int HeapEmpty(HP* hp);

我们主要讲一下删除和插入,其他的非常简单。

堆的插入

假设先插入一个10到数组的尾上,再进行向上调整算法,直到满足堆。

在这里插入图片描述
代码如下:

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

	//向上调整算法
	AdjustUp(php->a, php->size-1);
}

不知道向上调整算法的,请戳。

堆的删除

删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法,使数组满足堆的性质。

在这里插入图片描述
代码如下:

//删除
void HeapPop(HP* php)
{
	assert(php);
	assert(php->size);
	
	//交换
	Swap(&php->a[0], &php->a[php->size - 1]);

	//删除数据
	php->size--;

	//向下调整算法
	AdjustDown(php->a, php->size, 0);

}

不懂向下调整算法请戳。

其他接口

其他接口和顺序表差不多,这里给大家看一下代码。

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

//取对顶的数据
HPDateType HeapTop(HP* php)
{
	assert(php);
	assert(php->size);
	
	return php->a[0];
}

// 堆的数据个数
int HeapSize(HP* php)
{
	assert(php);

	return php->size;
}

// 堆的判空
int HeapEmpty(HP* php)
{
	assert(php);

	return php->size == 0;
}


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

}

那么堆的实现就是这么多的内容,重点是向上调整,向下调整算法,而向下调整算法是最最最重要的。

堆的应用

堆排序

我们先思考一个问题,排升序的话建大堆还是建小堆

答案是建大堆,有人就会有疑惑了,为什么要建大堆,问什么不建小堆呢,如果建小堆的话,那么堆顶的元素就是最小的,由于要排升序,我们就需要跳过第一个元素,但是后面的元素的父子关系就全乱了,需要重新建堆,而重新建堆的代价是非常大了,所以我们要建大堆,然后和删除一样,这时堆顶的元素是最大的,我们将堆顶的元素和最后一个元素换一下,然后使用向下调整算法,只不过需要将有效数据的个数减少一个就可以了。

排升序建大堆,那么排降序就是建小堆。

有人会说我们实现了堆,我们可以把数组的元素依次插入堆,然后依次按上面的操作,就可以实现排序了,最后再把数据拷回来就可以了。但是我们一般不这样玩,因为那样插入需要空间复杂度,而且把数据拷回来也是很挫的操作,我们一般都是在原数组之间建堆,我们可以用向上调整法建堆,也可以用向下调整法建堆。

向上调整法建堆

把数据都分割开,看出依次插入的,因为第一个数据就一个数据本身就是一个堆,所以直接从第二个数据开始就可以。

for (int i = 1; i < sz; i++)
{
	//这就和我们上面画的图想对应,依次插入,并且保证前面是堆
	//向上调整传的是数组和孩子节点(也就是需要调整的节点)
	AdjustUp(arr, i);
}

向下调整法建堆

向下调整的前提是两个孩子都是堆,所以我们可以从后往前调,而叶子节点不需要调,所以我们从最后一片叶子的父亲开始就可以。

for(int i = (sz - 1 - 1) / 2; i >= 0; i--)
{
	//sz是数组的大小
	//向下调整传的是数组,数组的大小,以及需要调的父亲节点
	AdjustDown(arr, sz, i);
}

我们搞清楚这个以后就可以开始我们的堆排序了。

1.我们需要建堆。
2.我们需要交换堆顶和最后一个元素的数据,然后进行向下调整算法。
由于我们建堆和调整数据都需要向下调整算法,所以我们掌握了向下调整算法就可以完成堆排序。

代码如下:

//交换函数
void Swap(int* p1, int* p2)
{
	int tmp = 0;
	tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

//向上调整算法
void AdjustUp(int* arr, int child)
{
	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 AdjustDown(int* arr, int sz, int parent)
{
	//假设是左孩子
	int child = 2 * parent + 1;
	while (child < sz)
	{
		if (child+1<sz && arr[child] < arr[child + 1])
		{
			child++;
		}
		
		if (arr[child] > arr[parent])
		{
			Swap(arr + child, arr + parent);
			parent = child;
			child = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}
}

//堆排序
void HeapSort(int* arr, int sz)
{
	//假设排升序,建大堆
	//向上调整算法建堆
	/*for (int i = 1; i < sz; i++)
	{
		AdjustUp(arr, i);
	}*/
	//向下调整算法建堆
	for(int i = (sz - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(arr, sz, i);
	}

	//交换收尾,接着向下调整算法
	int end = sz - 1;
	while (end > 0)
	{
		//交换首尾
		Swap(&arr[0], &arr[end]);
		//向下调整
		AdjustDown(arr, end, 0);
		end--;
	}
}

堆排序就讲到这里,有什么不理解的可以私信博主。

TopK问题

TopK是什么?

求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。
比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。

那这怎么解决呢?和堆又有什么关系呢?

对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能
数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决,基本思路如下:

  1. 用数据集合中前K个元素来建堆
    前k个最大的元素,则建小堆
    前k个最小的元素,则建大堆
  2. 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素

我这里用数组来给大家实现一下:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

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

void AdjustDown(int* arr, int sz, int parent)
{
	int child = parent*2+1;
	while (child < sz)
	{
		if (child+1<sz && arr[child] > arr[child + 1])
		{
			child++;
		}
		
		if (arr[parent] > arr[child])
		{
			Swap(&arr[parent], &arr[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
void PrintTopK(int* a, int n, int k)
{
	//直接在原数组的前K个建小堆
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, k, i);
	}

	int top = 0;
	for (int i = k; i < n; i++)
	{
		top = a[i];
		//取后k个元素依次和堆顶的元素比较,大的就替换,然后向下调整
		if(a[0] < top)
		{
			a[0] = top;
			AdjustDown(a, k, 0);
		}
	}

	for (int i = 0; i < k; i++)
	{
		printf("%d ", a[i]);
	}
}
void TestTopk()
{
	int n = 10000;
	int* a = (int*)malloc(sizeof(int) * n);
	srand((size_t)time(NULL));
	//生成一万个随机数
	for (int i = 0; i < n; ++i)
	{
		a[i] = rand() % 1000000;
	}
	a[5] = 1000000 + 1;
	a[1231] = 1000000 + 2;
	a[531] = 1000000 + 3;
	a[5121] = 1000000 + 4;
	a[115] = 1000000 + 5;
	a[2335] = 1000000 + 6;
	a[9999] = 1000000 + 7;
	a[76] = 1000000 + 8;
	a[423] = 1000000 + 9;
	a[3144] = 1000000 + 10;
	PrintTopK(a, n, 10);
	free(a);
}

int main()
{
	TestTopk();
	return 0;
}

那么堆讲到这里就结束了,今天的分享到这里也结束了,感谢大家的关注和支持。

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

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

相关文章

IntelliJ IDEA和Android studio怎么去掉usage和作者提示

截止到目前我已经写了 600多道算法题&#xff0c;其中部分已经整理成了pdf文档&#xff0c;目前总共有1000多页&#xff08;并且还会不断的增加&#xff09;&#xff0c;大家可以免费下载 下载链接&#xff1a;https://pan.baidu.com/s/1hjwK0ZeRxYGB8lIkbKuQgQ 提取码&#xf…

考虑微网新能源经济消纳的共享储能优化配置(Matlab代码实现

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

关于ArrayList的十三连问

文章目录 一、底层存储结构是什么二、初始容量三、构造方法四、扩容原理五、读写速度比较六、克隆为深克隆还是浅克隆七、多线程环境下是否安全八、增强遍历时添加或删除元素会发生什么事情九、为什么数组被transient修饰十、通过subList()获得的集合能否转为ArrayList十一、使…

基于灰狼优化(GWO)、帝国竞争算法(ICA)和粒子群优化(PSO)对梯度下降法训练的神经网络的权值进行了改进(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【python】一文了解Python爬虫 | 文末送书

目录 引言 1. 爬虫基础知识 1.1 什么是爬虫 1.2 HTTP协议 1.2.1 HTTP请求方法 1.GET请求 1.2.2 请求头常见字段 1.2.3 响应状态码 1.3 HTML解析 1.3.1 Beautiful Soup 解析库 1.3.2 XPath xpath解析原理: xpath 表达式 2. 爬虫进阶技巧 2.1 防止被反爬虫 2.1.1 …

信息论基础知识

注意&#xff1a;本文只针对离散随机变量做出探讨&#xff0c;连续随机变量的情况不适用于本文探讨的内容&#xff01; &#xff08;一&#xff09;自信息 1. 自信息 I ( x ) − l o g n P ( x ) \color{blue}I(x) - log_{n}{P(x)} I(x)−logn​P(x) 注意&#xff1a; 若n …

用ChatGPT和六顶帽思考法帮助自己更好地决策和解决问题

当我们在解决复杂问题时&#xff0c;我们常常陷入单一视角的状态。创造性思维领域的先驱爱德华德博诺&#xff0c;提出了六顶帽思考法[1]&#xff0c;这意味着我们可以从六个不同的视角来思考一个问题&#xff0c;以实现高水平决策和解决问题。 每一顶“帽子”代表不同的视角。…

【Matlab】PSO优化(单隐层)BP神经网络算法

上一篇博客介绍了BP-GA&#xff1a;BP神经网络遗传算法(BP-GA)函数极值寻优——非线性函数求极值&#xff0c;本篇博客将介绍用PSO&#xff08;粒子群优化算法&#xff09;优化BP神经网络。 1.优化思路 BP神经网络的隐藏节点通常由重复的前向传递和反向传播的方式来决定&#…

UGUI源码深度剖析

源码下载后 直接嵌入工程&#xff0c;删除引擎extension里的&#xff1b; 自制UI&#xff0c;在一个空场景中显示一个图片&#xff0c;当鼠标点击图片&#xff0c;执行操作。 gameobject &#xff1a; mesh meshfilter meshrender maintexture meshcollider camera ray

基于SSM的智能商城购物系统

基于SSM的智能商城购物系统 项目简介项目获取开发环境项目技术运行截图 项目简介 该智能商城系统主要实现两大功能模块:前台管理和后台管理 前台管理包括五大模块:用户登录注册、商品信息、购物车信息、个人信息管理、下单与订单管理、订单物流设置。 (1)用户登录注册模块:该功…

【Python机器学习】实验10 支持向量机

文章目录 支持向量机实例1 线性可分的支持向量机1.1 数据读取1.2 准备训练数据1.3 实例化线性支持向量机1.4 可视化分析 实例2 核支持向量机2.1 读取数据集2.2 定义高斯核函数2.3 创建非线性的支持向量机2.4 可视化样本类别 实例3 如何选择最优的C和gamma3.1 读取数据3.2 利用数…

大数据Flink(六十一):Flink流处理程序流程和项目准备

文章目录 Flink流处理程序流程和项目准备 一、Flink流处理程序的一般流程

使用jasypt对Spring Boot配置文件中的配置项加密

在Spring Boot中&#xff0c;有很多口令需要加密&#xff0c;如数据库连接密码、访问第三方接口的Token等。常见的方法就是用jasypt对口令进行加密。 实际上&#xff0c;jasypt可以对配置文件中任意配置项的值进行加密&#xff0c;不局限于对密码的加密。 1.在pom.xml中添加ja…

21 | 朝阳医院数据分析

朝阳医院2018年销售数据为例,目的是了解朝阳医院在2018年里的销售情况,通过对朝阳区医院的药品销售数据的分析,了解朝阳医院的患者的月均消费次数,月均消费金额、客单价以及消费趋势、需求量前几位的药品等。 import numpy as np from pandas import Series,DataFrame impo…

友善之臂NanoPi NEO利用fbtft驱动点亮1.69寸ST7789V2屏幕

屏幕介绍 本文以中景园1.69寸LCD&#xff0c;驱动芯片ST7789V2该款屏幕示例&#xff0c;屏幕的分辨率为240*280 屏幕引脚说明 NanoPi NEO IO介绍 屏幕与板子的IO连接关系 屏幕NanoPi NEOGNDGNDVCC3.3VSCLPC2SDAPC0RESPG11DCPA1CSPC3BLKPA0 下载交叉编译器和linux内核源码并按教…

c语言——完数的计算

完数即所有因子之和等于其本身值 列入&#xff0c;28124714&#xff0c;28所有的因子为1&#xff0c;2&#xff0c;4&#xff0c;7&#xff0c;14 而这五个因子之和恰好也是28. //完数的计算 /*完数即所有因子之和等于其本身值 列入&#xff0c;28124714&#xff0c;28所有的…

LeetCode700. 二叉搜索树中的搜索

700. 二叉搜索树中的搜索 文章目录 [700. 二叉搜索树中的搜索](https://leetcode.cn/problems/search-in-a-binary-search-tree/)一、题目二、题解方法一&#xff1a;迭代方法二&#xff1a;递归 带main函数测试用例 一、题目 给定二叉搜索树&#xff08;BST&#xff09;的根节…

在线状态监测如何使冷却塔维保管理受益

工业冷却塔作为关键的热交换装置&#xff0c;在许多生产流程中发挥着重要作用。为了保持其高效稳定的运行&#xff0c;实施连续的冷却塔状态监测变得至关重要。本文将以PreMaint设备数字化平台为例&#xff0c;探讨为什么建议采用远程冷却塔状态监测&#xff0c;以及如何借助振…

PHP证券交易员学习网站mysql数据库web结构apache计算机软件工程网页wamp

一、源码特点 PHP证券交易员学习网站 是一套完善的web设计系统&#xff0c;对理解php编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。 下载地址https://download.csdn.net/download/qq_41221322/88205549 PHP证券交易员…

Python爬虫的requests(学习于b站尚硅谷)

目录 一、requests  1. requests的基本使用  &#xff08;1&#xff09;文档  &#xff08;2&#xff09;安装  &#xff08;3&#xff09;响应response的属性以及类型  &#xff08;4&#xff09;代码演示 2.requests之get请求  3. requests之post请求  &#x…