【数据结构】(堆)Top-k|堆排序

news2024/11/26 16:23:50

目录

概念:

堆的实现 

构建

初始化

销毁

插入元素

往上调整 

删除堆顶元素 

往下调整 

返回堆顶元素 

 返回有效个数

是否为空

 堆排序

 Top-k问题

​编辑 创建数据

堆top-k


概念:

堆是将数据按照完全二叉树存储方式存储到一维数组中;

堆分为大堆和小堆:

大堆:父结点大于等于孩子结点;

小堆:父结点小于等于孩子结点;

父结点与(左右)孩子结点关系:

1.父结点 = (孩子结点-1)/2;

2.左结点= (父结点*2)+1;

        右结点= (父结点*2)+2;

堆的实现 

堆的逻辑结构是完全二叉树,物理结构是一维数组存储;

而独特的结点关系,堆排序也是一种选择排序,

构建

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* parr;
	int size;		//存储的有效数据个数
	int capacity;	//容量
}Heap;
//	用数组存储    

初始化

//堆的初始化
void HeapInit(Heap* php)
{
	assert(php);
	php->parr = NULL;
	php->size = 0;
	php->capacity = 0;
}

销毁


//堆的销毁
void HeapDestroy(Heap* php)
{
	assert(php);
	free(php->parr);
	php->parr = NULL;
	php->size = php->capacity = 0;
	free(php);
	php = NULL;
}

插入元素

因为堆分为两类,在数据插入时,需要选择适应的调整;

以小堆来说:当插入一个新元素时,插入到堆尾,与父结点比较,相应的往上调整

//堆的插入元素
void HeapPush(Heap* php, HPDataType x)
{
	assert(php);
	//检查容量
	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* newparr = (HPDataType*)realloc(php->parr, sizeof(HPDataType) * newcapacity);
		if (newparr == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		php->capacity = newcapacity;
		php->parr = newparr;
	}
	php->parr[php->size] = x;
	php->size++;
	
	//小堆
	//向上调整
	AdjustUp(php->parr, php->size - 1);
}

往上调整 

当插入一个新元素,按照孩子和父结点之间的关系进行比较,交换两结点数据,直到满足堆的性质 

//向上调整
void AdjustUp(HPDataType* parr,int size)
{
	int child = size;
	int parent = (child - 1) / 2;
	//小堆=> 父结点<=孩子结点
	while (child>0)
	{
		if (parr[child] < parr[parent])
		{
			//交换数据
			Swap(&parr[child], &parr[parent]);
			child = parent;        //更新结点位置
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

删除堆顶元素 

1.将堆顶元素和尾部元素互换位置;

2.将此刻不符合规定的堆顶元素往下调整至相应位置; 

// 删除堆顶(根节点)
void HeapPop(Heap* php)
{
	assert(php);
	//1.堆顶元素和尾部元素置换位置
	Swap(&php->parr[0], &php->parr[php->size - 1]);
	php->size--;	//删掉交换后的堆顶元素

	//2.将新站顶元素找到相应位置
	//向下调整
	AdjustDown(php->parr,php->size,0);
}

往下调整 

堆顶元素与其孩子结点比较,如何找到较大(较小)的孩子?

可以假设法:假设较大(较小)的孩子为左孩子,然后验证假设;

//向下调整
void AdjustDown(HPDataType* parr,int size,int parent)
{
	int child = (parent * 2) + 1;
	while (child<size)	//
	{
		//假设左孩子为较小值
		if (child+1<size && parr[child + 1] < parr[child])	//验证假设
		{
			++child;	//说明右孩子更小,更换孩子位置
		}
		//检查是否不符合堆排序结构 
		if (parr[parent] > parr[child])
		{
			Swap(&parr[parent], &parr[child]);
			//往下更新父结点 孩子结点位置
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

返回堆顶元素 

起始值为0;

//返回堆顶元素
HPDataType HeapTop(Heap* php)
{
	assert(php);
	assert(php->size > 0);
	return php->parr[0];
}

 返回有效个数

注意,构建堆的时候,size是最后一个元素的下一个;

//返回堆内有效数据个数
size_t HeapSize(Heap* php)
{
	assert(php);

	return php->size;	//数组下标0开始
}

是否为空

//判断堆是否为空
bool HeapEmpty(Heap* php)
{
	return php->size == 0;
}

 堆排序

以上是一些堆的简单功能实现;算不上真正的堆排序;

假设给定一串数字,4,6,2,1,5,8,2,9;如何将其排序?比如升序;

  1. 建立一个大堆;
  2. 将堆顶元素与堆尾元素互换,且将遍历堆的范围-1,保证其想要的值保持不动;
  3. 将此刻不符合规定的堆顶往下调整,找到次大的值;重复步骤2;

其实相当于第一个元素默认是堆,后面的进行遍历调整; 

//排序,升序
void HeapSort(int* parr, int n)
{
	//1.建立大堆
	for (int i = 1;i < n; i++)
	{
		justUp(parr, i);
	}

	//2.堆顶元素与堆尾元素互换,然后将堆size-1(指只需要遍历到的位置)
	int end = n - 1;
	while (end>0)
	{
		//堆顶和堆尾 元素呼唤
		Swap(&parr[0], &parr[end]);
		//往下调整
		justDown(parr,end,0);
		end--;
	}
}

也有其他思路;

我们从下面子树往上遍历,而内部调整时往下调整

 n-1是最后结点下标值,(结点-1)/2 可以得到该结点的父结点,从父结点往下调整;

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

 Top-k问题

在排序的基础上,如果这个数很大呢,比如一百万个数,要找到前k个较大值;

此刻建堆排序显然不合适;

1.读取前K个值,建立其小堆;

2.依次读取后面的值,与堆顶比较:如果比堆顶大,替换堆顶进堆,再往下调整;

 创建数据

//tok-k 问题
//创建一千万的数据
void CreateNode()
{
	// 造数据
	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;	//+i是 因为随机数产生只有三万个,加上i可以大大减少重复值
		fprintf(fin, "%d\n", x);
	}

	fclose(fin);
}

堆top-k

开辟K个数的空间(动态数组);

建立K个数的小堆;

读取文件中值,遍历与堆顶比较,

void HeapTok(const char* file,int k)
{
	FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		perror("fopen error");
		return;
	}

	//开辟K个数的空间
	int* minheap = (int*)malloc(sizeof(int) * k);
	if (minheap == NULL)
	{
		perror("malloc error");
		return;
	}

	//建立K个数的小堆
	for (int i = 0; i < k; i++)
	{
		//从文件流中 读取数据存到 开辟的空间中
		fscanf(fout,"%d", &minheap[i]);
		//往上调整
		AdjustUp(minheap, i);
	}

	//遍历文件数据 
	int x = 0;
	while (fscanf(fout, "%d", &x) != EOF)	
	{
		if (x > minheap[0])
		{
			minheap[0] = x;
			//往下调
			AdjustDown(minheap, k, 0);
		}
	}

	//打印tok
	for (int i = 0; i < k; i++)
	{
		printf("%d ", minheap[i]);
	}
	free(minheap);
	fclose(fout);
}

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

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

相关文章

Python实验项目9 :网络爬虫与自动化

实验 1&#xff1a;爬取网页中的数据。 要求&#xff1a;使用 urllib 库和 requests 库分别爬取 http://www.sohu.com 首页的前 360 个字节的数据。 # 要求&#xff1a;使用 urllib 库和 requests 库分别爬取 http://www.sohu.com 首页的前 360 个字节的数据。 import urllib.r…

网络 / day02 作业

1. TCP和UDP通信模型 1.1 TCP server #include <myhead.h>#define PORT 9999 #define IP "192.168.250.100"int main(int argc, const char *argv[]) {//1. create socketint sfd -1;if( (sfd socket(AF_INET, SOCK_STREAM, 0 ))-1 ){perror("socke…

YOLOv5改进 | SPPF | 将RT-DETR模型AIFI模块和Conv模块结合替换SPPF(全网独家改进)

一、本文介绍 本文给大家带来是用最新的RT-DETR模型中的AIFI模块来替换YOLOv5中的SPPF。RT-DETR号称是打败YOLO的检测模型&#xff0c;其作为一种基于Transformer的检测方法&#xff0c;相较于传统的基于卷积的检测方法&#xff0c;提供了更为全面和深入的特征理解&#xff0c…

机器学习 | KNN算法

一、KNN算法核心思想和原理 1.1、怎么想出来的&#xff1f; 近朱者赤&#xff0c;近墨者黑&#xff01; 距离决定一切、民主集中制 1.2、基本原理 —— 分类 k个最近的邻居 民主集中制投票分类表决与加权分类表决 1.3、基本原理 —— 回归 计算未知点的值决策规则不同均值法与…

Vue2-动态组件案例

1.component介绍 说明&#xff1a; Type: string | ComponentDefinition | ComponentConstructor Explanation: String: 如果你传递一个字符串给 is&#xff0c;它会被视为组件的名称&#xff0c;用于动态地渲染不同类型的组件。这是一个在运行时动态切换组件类型的常见用例。…

西南科技大学数字电子技术实验六(智力竞赛抢答器的设计及FPGA实现)FPGA部分

一、实验目的 进一步掌握D触发器&#xff0c;分频电路&#xff0c;多谐振荡电器&#xff0c;CP时钟脉冲元等单元电路的设计。 二、实验原理 三、程序清单&#xff08;每条语句必须包括注释或在开发窗口注释后截图&#xff09; 逻辑代码&#xff1a; module contend ( inpu…

OpenCV 1.1:读取本地图片显示灰度图及CV版本

imread方法加载图像 imread 功能是加载图像文件成为一个 Mat 对象&#xff0c;其中第一个参数表示图像文件名称&#xff0c;第二个参数表示加载的图像是什么类型&#xff0c;支持常见的三个参数值IMREAD_UNCHANDED(<0) 表示加载原图&#xff0c;不做任何改变IMREAD_GRAYSCA…

Docker插件和扩展:深入Docker功能的完整指南

Docker作为一种流行的容器化技术&#xff0c;不仅令应用程序的部署更为便捷&#xff0c;同时也提供了丰富的插件和扩展机制&#xff0c;以满足更多复杂场景下的需求。本文将深入研究Docker的插件和扩展&#xff0c;提供更为详实和全面的示例代码&#xff0c;助力读者更好地理解…

DPO讲解

PPO算法的pipeline冗长&#xff0c;涉及模型多&#xff0c;资源消耗大&#xff0c;且训练极其不稳定。DPO是斯坦福团队基于PPO推导出的优化算法&#xff0c;去掉了RW训练和RL环节&#xff0c;只需要加载一个推理模型和一个训练模型&#xff0c;直接在偏好数据上进行训练即可&am…

计算机出现xinput1_3.dll缺失、五种详细解决方案

在电脑使用过程中&#xff0c;我们可能会遇到一些错误提示&#xff0c;其中之一就是xinput1_3.dll丢失。这个错误提示通常会导致游戏或其他应用程序无法正常运行。那么&#xff0c;xinput1_3.dll文件到底是什么&#xff1f;它丢失会对电脑产生什么影响&#xff1f;本文将为您详…

1688订单详情对接及实现方案

一、引言 1688作为中国最大的B2B电子商务平台之一&#xff0c;提供了丰富的商品信息和订单详情。通过与1688订单详情接口的对接&#xff0c;电商企业可以实时获取订单详细信息&#xff0c;以便更好地了解客户需求、优化运营策略以及提高服务质量。本文将详细介绍如何实现1688订…

《数据结构、算法与应用C++语言描述》-机器调度-最长处理时间(LPT)-堆应用

机器调度 完整可编译运行代码见&#xff1a;Github::Data-Structures-Algorithms-and-Applications/_28LongestProcessingTime 问题描述 一个工厂具有 m台一模一样的机器。我们有n 个任务需要处理。设作业i的处理时间为 t i t_i ti​&#xff0c;这个时间包括把作业放入机器…

【PostgreSQL】从零开始:(十二)PostgreSQL-数据库对象关系及定义

从图中我们可以看出服务器对象的关系 现在我们来了解它们的定义 数据库服务器(Server) 数据库服务器是一种用来存储、管理和查询大量数据的服务器。它是一个独立的计算机系统&#xff0c;运行数据库管理系统&#xff08;DBMS&#xff09;软件&#xff0c;并提供对数据库的访问…

【ECMAScript笔记二】运算符分类,流程控制(顺序结构、分支结构、循环结构)

文章目录 4 运算符4.1 算术运算符4.2 递增和递减运算符4.3 比较运算符4.4 逻辑运算符4.5 赋值运算符4.6 运算优先级 5 流程控制5.1 顺序结构5.2 分支结构5.2.1 if 语句5.2.2 switch 语句 5.3 循环结构5.3.1 for循环5.3.2 while循环5.3.3 do while循环5.3.4 continue和break 5.4…

数据科学知识库

​ 我的博客是一个技术分享平台&#xff0c;涵盖了机器学习、数据可视化、大数据分析、数学统计学、推荐算法、Linux命令及环境搭建&#xff0c;以及Kafka、Flask、FastAPI、Docker等组件的使用教程。 在这个信息时代&#xff0c;数据已经成为了一种新的资源&#xff0c;而机…

1.新入手的32位单片机资源和资料总览

前言&#xff1a; 学了将近1年的linux驱动和uboot&#xff0c;感觉反馈不足&#xff0c;主要是一直在学各种框架&#xff0c;而且也遇到了门槛&#xff0c;比如驱动部分&#xff0c;还不能随心所欲地编程&#xff0c;原因是有些外设的原理还不够深刻、有些复杂的底层驱动的代码…

java springboot 内存级数据库 H2 创建表并添加数据演示

好 上文 java简述springboot通过配合初始化H2数据库并完成登录 带着大家登进了 h2数据库 这里需要强调 你只需要第一次加上 datasource:url: jdbc:h2:~/testhikari:driver-class-name: org.h2.Driverusername: rootpassword: 123456这些 因为它要初始化 你后面再启动 去掉这些…

C语言预处理详解及其指令

预处理详解 1.预定义符号2.#define定义常量基本使用方法举例子如果在define定义的表示符后面加上分号会发生什么&#xff1f;用一下来解释 3. #define定义宏举例例1例2 4. 带有副作用的宏参数例如: 5. 宏替换的规则6. 宏函数的对比宏和函数的一个对比 7. #和##7.1 #运算符7.2 #…

shell子进程管理

简介 在我们平时写代码过程中&#xff0c;可能经常会遇到串行执行速度慢 &#xff0c;串行无法执行多个任务&#xff0c;这时便需要使用子进程同时执行。使用父进程创建子进程时&#xff0c;子进程会复制父进程的内存、文件描述符和其他相关信息。当然&#xff0c;子进程可以独…

2023年全球运维大会(GOPS深圳站)-核心PPT资料下载

一、峰会简介 1、大会背景与概述 全球运维大会&#xff08;GOPS&#xff09;是运维领域最具影响力的国际盛会&#xff0c;每年都会汇聚世界各地的运维专家、企业领袖、技术爱好者&#xff0c;共同探讨运维技术的最新发展、最佳实践以及面临的挑战。2023年GOPS深圳站作为该系列…