【数据结构】堆(Heap):堆的实现、堆排序、TOP-K问题

news2024/11/17 15:46:47

目录

堆的概念及结构

​编辑

堆的实现 

实现堆的接口

堆的初始化

堆的打印

堆的销毁

获取最顶的根数据

 交换

堆的插入(插入最后)

向上调整(这次用的是小堆)

堆的删除(删除根)

向下调整(这次用的小堆)

堆排序

TOP-K问题


堆的概念及结构

如果有一个关键码的集合 K = { , , , ,},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足: <= 且 <= ( >= 且 >= ) i = 0, 1 , 2…,则称为小堆 ( 或大堆 ) 。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
堆的性质:
  • 堆中某个节点的值总是不大于或不小于其父节点的值;
  • 堆总是一棵完全二叉树。

小根堆:父亲节点大于等于孩子节点

大根堆:父亲节点小于等于孩子节点 

堆的实现 

实现堆的接口

#define CRT_SECURE_NO_WARNING 1
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<stdbool.h>

//二叉树-堆
typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}HP;


void AdjustUp(HPDataType* a, int child);

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

//交换
void Swap(HPDataType* p1, HPDataType* p2);
//打印
void HeapPrint(HP* php);
//初始化
void HeapInit(HP* php);
//
void HeapInitArray(HP* php, int* a, int n);
//销毁
void HeapDestroy(HP* php);
//插入
void HeapPush(HP* php, HPDataType x);
//删除
void HeapPop(HP* php);
//返回最顶数据
HPDataType HeapTop(HP* php);
//判空
bool HeapEmpty(HP* php);

堆的初始化

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

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

堆的打印

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

	//最后一个孩子下标为size-1
	for (size_t i = 0; i < php->size; i++)
	{
		printf("%d ", php->a[i]);
	}
	printf("\n");
}

堆的销毁

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

	free(php->a);
	php->a = NULL;
	php->size = php->capacity = 0;
}

获取最顶的根数据

//获取根数据
HPDataType HeapTop(HP* php)
{
	assert(php);
	assert(php->size > 0);

	return php->a[0];
}

 交换

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

堆的插入(插入最后)

先考虑扩容,将数据插到最后,再用向上调整法。

//插入数据
void HeapPush(HP* php, HPDataType x)
{
	assert(php);

	//扩容
	if (php->size == php->capacity)//有效元素个数和容量是否相等
	{
		//相等的情况分两种:1.容量为0,先扩4个sizeof   2.容量占用满了,扩2个
		int newCapacity =php->capacity == 0 ? 4 : php->capacity * 2;
		//返回扩容后的内存新地址							//扩容后的新大小
		HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * newCapacity);
		
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}

		//扩容后的新地址
		php->a = tmp;
		//新容量
		php->capacity = newCapacity;
	}

	//   php->size下标位置  先将x放最后,后面再调整
	php->a[php->size] = x;
	//   有效数据++
	php->size++;
	//   向上调整     //size-1为新插入数据的下标
	AdjustUp(php->a, php->size - 1);

}

向上调整(这次用的是小堆)

向上调整的前提:左右子树是堆 ,时间复杂度O(logN)

//向上调整                    //新插入的数据下标
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 = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

堆的删除(删除根)

先判空,看下是否还有元素可以删除。根数据先和最后一个孩子交换位置,孩子再向下调整。

//删除
void HeapPop(HP* php)
{
	assert(php);
	//确保有元素可删
	assert(php->size > 0);

	//最后一个孩子和要删除的根交换
	Swap(&php->a[0], &php->a[php->size - 1]);
	//有效元素size减减,相当于删除了交换后的原来的根
	--php->size;

	//删除后向下调整
	AdjustDown(php->a, php->size, 0);

}

向下调整(这次用的小堆)

向下调整的前提:左右子树是堆 

//向下调整
void AdjustDown(HPDataType* a, int n, int parent)
{
	int child = parent * 2 + 1;
	//n下标位置已经没有数了
	while (child < n)
	{
		//选小的孩子往上浮(小堆)
		if (child + 1 < n && a[child + 1] < a[child])
		{
			++child;
		}
		//若小的孩子都小于父,则交换
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			//交换后下来的数重新变成parent,继续向下调整
			parent = child;
			child = parent * 2 + 1;
		}
	}
}

堆排序

1. 建堆
升序:建大堆
降序:建小堆
2. 利用堆删除思想来进行排序
建堆:向上调整法建堆的时间复杂度:O(N*logN)
           向下调整法建堆的时间复杂度:O(N)
可以用堆删除思想向下调整法将栈顶和最后一个元素交换,依次将最大的次大的......往后放,就达到了升序排列。
void HeapSort(int* a, int n)
{
	//建堆  这里可以选建大堆还是小堆
	// 向下调整建堆
	// O(N)
	for (int i = (n-1-1)/2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}

	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		--end;
	}
}

TOP-K问题

TOP-K 问题:即求数据结合中前 K 个最大的元素或者最小的元素,一般情况下数据量都比较大
比如:专业前 10 名、世界 500 强、富豪榜、游戏中前 100 的活跃玩家等。
对于 Top-K 问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了 ( 可能
数据都不能一下子全部加载到内存中 ) 。最佳的方式就是用堆来解决,基本思路如下:
1. 用数据集合中前 K 个元素来建堆
k 个最大的元素,则建小堆
k 个最小的元素,则建大堆
2. 用剩余的 N-K 个元素依次与堆顶元素来比较,不满足则替换堆顶元素
将剩余N-K 个元素依次与堆顶元素比完之后,堆中剩余的 K 个元素就是所求的前 K 个最小或者最大的元素

先创建一个包含有10000000个数的data.txt文本文件。

void CreateNDate()
{
	// 造数据
	int n = 10000000;
	srand(time(0));
	const char* file = "data.txt";
	FILE* fin = fopen(file, "w");
	if (fin == NULL)
	{
		perror("fopen error");
		return;
	}

	for (int i = 0; i < n; ++i)
	{
		int x = (rand() + i) % 10000000;
		fprintf(fin, "%d\n", x);
	}

	fclose(fin);
}

前k个建小堆(堆顶元素为k中最小),剩余n-k个依次和堆顶元素比较,比k大就插入堆中(插入堆插入向下调整法),完成后打印前k个元素。

void PrintTopK(const char* filename, int k)
{
	// 1. 建堆--用a中前k个元素建堆
	FILE* fout = fopen(filename, "r");
	if (fout == NULL)
	{
		perror("fopen fail");
		return;
	}

	//给堆开辟空间
	int* minheap = (int*)malloc(sizeof(int) * k);
	if (minheap == NULL)
	{
		perror("malloc fail");
		return;
	}

	for (int i = 0; i < k; i++)
	{
		fscanf(fout, "%d", &minheap[i]);
	}

	// 前k个数建小堆
	for (int i = (k - 2) / 2; i >= 0; --i)
	{
		AdjustDown(minheap, k, i);
	}


	// 2. 将剩余n-k个元素依次与堆顶元素交换,不满则则替换
	int x = 0;
	while (fscanf(fout, "%d", &x) != EOF)
	{
		if (x > minheap[0])
		{
			// 替换你进堆
			minheap[0] = x;
			AdjustDown(minheap, k, 0);
		}
	}


	for (int i = 0; i < k; i++)
	{
		printf("%d ", minheap[i]);
	}
	printf("\n");

	free(minheap);
	fclose(fout);
}

假设k等于5,成功打印出前5个最大的数据

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

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

相关文章

2013年6月26日 Go生态洞察:Go Race Detector的探索

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

centos下安装mysql8版本

1、如果服务器没有wget&#xff0c;先下载wget工具 sudo yum install wget 2、下载指定mysql版本的tar包 sudo wget https://downloads.mysql.com/archives/get/p/23/file/mysql-8.0.21-1.el7.x86_64.rpm-bundle.tar 3、解压tar包 sudo tar -xvf mysql-8.0.21-1.el7.x86_64.rpm…

现代浴室:从硬朗到柔美

现代浴室不再是冰冷的地方&#xff0c;而是一个温馨的角落。它是我们日常生活中的庇护所&#xff0c;一个能够唤起宁静和舒适感的地方。如今&#xff0c;浴室的设计注重的是颜色、材质、光线和表面的精心搭配&#xff0c;以创造一个柔和、温馨的空间。 与过去的硬朗陶瓷、锋利…

全国犯罪人数大数据可视化平台【可视化项目案例-08】

🎉🎊🎉 你的技术旅程将在这里启航! 🚀🚀 本文选自专栏:可视化技术专栏100例 可视化技术专栏100例,包括但不限于大屏可视化、图表可视化等等。订阅专栏用户在文章底部可下载对应案例源码以供大家深入的学习研究。 🎓 每一个案例都会提供完整代码和详细的讲解,不…

工厂自动化中DCS软件

概述 Monitor.Analog是新一代运行监控系统&#xff0c;是物联网时代数据驱动的智能工厂的神经中枢。通过连接到阿自倍尔专有的在线故障预测系统&#xff08;该系统利用 AI&#xff08;人工智能&#xff09;&#xff09;以及利用来自各个智能设备的监控和诊断数据的系统&#x…

Spring Boot EasyPOI 使用指定模板导出Excel

相信大家都遇到过&#xff0c;用户提出要把界面上的数据导成一个Excel&#xff0c;还得是用户指定的Excel格式&#xff0c;用原生的POI&#xff0c;需要自己去实现&#xff0c;相信是比较麻烦的&#xff0c;所以我们可以使用开源的EasyPOI. 先上个图&#xff0c;看看是不是大家…

第三篇 基于JSP 技术的网上购书系统—— 数据库系统设计(网上商城、仿淘宝、当当、亚马逊)

目录 1.逻辑关系设计 2.物理设计 2.1管理员表 2.2留言表 2.3会员登录表 2.4会员表 2.5订单表 2.6订单商品表 2.7产品表 2.8产品货架表 2.9收藏表 2.10类别表 2.11新闻表 数据库系统是用来保存数据的软件系统&#xff0c;当今比较流行的数据库系统&#xff0c;如 MS…

Linux在线安装MySQL8.0.24安装、MySQL数据备份和恢复

一、 Linux在线安装MySQL8.0.24 如果机器上已经有MySQL5.7版本需要先卸载 首先&#xff0c;需要停止MySQL服务。可以通过以下命令来停止服务&#xff1a; sudo systemctl stop mysqld接下来&#xff0c;我们需要卸载MySQL5.7。可以通过以下命令来卸载&#xff1a; sudo yum…

为什么串行通信中停止位(停止bit、stop bit)通常使用高电平?

文章目录 基于高电平的停止位选择&#xff1a;理解其深层原因引言数据通信和停止位数据通信简介停止位的定义和作用 为什么选择高电平作为停止位&#xff1f;硬件的考虑误码率的影响 总结参考资料 基于高电平的停止位选择&#xff1a;理解其深层原因 引言 在数字通信中&#…

WPF小知识

在编写WPF程序遇到一些小问题&#xff0c;所以记录起来&#xff0c;查其他方便。 Label自动换行 网上搜的都不能自动换行&#xff0c;发现使用Run 就可以。在脚本中直接调用labTip.Text进行赋值就可以了。 <Label Foreground"#FF9E9E9E" FontSize"16"…

人工智能基础_机器学习035_多项式回归升维实战2_使用sklearn的PolynomialFeatures进行升维---人工智能工作笔记0075

我们再来做一个升维处理,这里我们不再自己去对数据进行比如,相乘操作,来给数据手动添加维度了, 这里我们用sklearn库提供的PolynomialFeatures来自动对数据进行升维. from sklearn.linear_model import LinearRegression # PolynowlalFeatures,多项式升维处理 from sklearn.…

一招学会!柴油发电机监测真的超级简单!

随着现代社会对电力的不断需求和对可靠性的高要求&#xff0c;柴油发电机的监控变得愈发重要。监控系统的引入不仅可以提高柴油发电机的运行效率&#xff0c;还能有效预防潜在故障&#xff0c;确保系统稳定运行。 客户案例 工业生产场 在工业生产场&#xff0c;柴油发电机是关…

小红书电商文案怎么创作,撰写事项!

电商文案就像是连接着品牌方与消费者之间的桥梁。优秀的电商文案&#xff0c;能够帮助消费者在第一时间内&#xff0c;了解产品&#xff0c;与品牌产生链接&#xff0c;最终带来消费转化。那么小红书电商文案怎么创作&#xff0c;撰写事项&#xff01;今天来马文化传媒为大家总…

Java项目maven打包,打jar包中不包含项目引用第三方jar包,以及打war包不能将其放到lib的问题

在使用maven进行打包项目中&#xff0c;想要将第三方的jar包放入&#xff0c;有两种方法&#xff1a;一种将jar包上传到maven库中&#xff0c;第二种再pom.xml中进行配置&#xff0c;第三种 情况是需要打包成war包放入tomcat中&#xff1b;具体如下&#xff1a; 第一种&#x…

MyEclipse 2017 安装与pj

关于Myeclipse 2017的安装与破解 MyEclipse企业级工作平台&#xff08;MyEclipseEnterprise Workbench &#xff0c;简称MyEclipse&#xff09;是对EclipseIDE的扩展&#xff0c;利用它我们可以在数据库和JavaEE的开发、发布以及应用程序服务器的整合方面极大的提高工作效率。…

【带头学C++】----- 六、结构体 ---- 6.6 结构体的指针成员

6.5结构体指针变量 结构体的指针变量:本质是变量只是该变量保存的是结构体变量的地址 6.5.1结构体指针变量的定义 通过指针&#xff0c;可以访问到我们结构体变量的值 可以通过 -> 符号 访问到结构体变量 6.5.2 结构体数组元素的指针变量 指针变量保存结构体数组元素…

Django视图层

视图层 django视图层&#xff1a;Django项目下的views.py文件&#xff0c;它的内部是一系列的函数或者是类,用来处理客户端的请求后处理并返回相应的数据 三板斧 HttpResponse # 返回字符串 render # 返回html页面&#xff0c;并且在返回浏览器之前还可以给html文件…

《网络协议》06. HTTP 补充 · HTTPS · SSL/TLS

title: 《网络协议》06. HTTP 补充 HTTPS SSL/TLS date: 2022-10-06 18:09:55 updated: 2023-11-15 07:53:52 categories: 学习记录&#xff1a;网络协议 excerpt: HTTP/1.1 协议的不足、HTTP/2、HTTP/3、HTTP 协议的安全问题、SPDY、HTTPS、SSL/TLS、OpenSSL。 comments: fa…

数据分析法宝,一个 SQL 语句查询多个异构数据源

随着企业数据量呈现出爆炸式增长&#xff0c;跨部门、跨应用、跨平台的数据交互需求越来越频繁&#xff0c;传统的数据查询方式已经难以满足这些需求。同时&#xff0c;不同数据库系统之间的数据格式、查询语言等都存在差异&#xff0c;直接进行跨库查询十分困难。 原生跨库查…

基础课3——客服中心现状

智能客服服务的对象就是客服中心&#xff0c;智能客服旨在帮助客服中心更好、更快地解决客户的问题。 1.客服中心的背景 随着数字化时代的到来&#xff0c;客户服务已经成为了企业中不可或缺的一部分。消费者对于客户服务的期望也在不断变化&#xff0c;他们不再满足于仅仅在…