数据结构之堆(topk问题、堆排序)

news2025/6/4 17:22:33

一、堆的初步认识

堆虽然是用数组存储数据的数据结构,但是它的底层却是另一种表现形式。

堆分为大堆和小堆,大堆是所有父亲大于孩子,小堆是所有孩子大于父亲。

 通过分析我们能得出父子关系的计算公式,parent=(child-1)/2,左孩子leftchild=parent*2+1,右孩子rightchild=parent*2+2,这会在下面实现时应用。

二、堆实现

1、头文件

#pragma once
#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 HeapPrint(HP* php);//堆打印
void HeapInit(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(HPDataType* a, int n, int parent);//向下调整
void Swap(HPDataType* x, HPDataType* y);//交换函数
void HeapSort(int* arr, int n);//堆排序
void HeadDestroy(HP* php);//堆销毁

这里用结构体管理数据,HPDataType*a是动态数组的指针,HPDataType是typedef定义的可变参数,需要更改存储类型时,修改typedef的内容即可,size用于统计存储数据的多少,capacity是统计开辟的空间大小,即动态申请数组的大小。 

2、 堆初始化

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

	php->a = (HPDataType*)malloc(sizeof(HPDataType)* 4);
	if (php->a == NULL)
	{
		perror("malloc fail");
		return;
	}

	php->size = 0;
	php->capacity = 4;
}

assert检查指针是否为空,malloc动态申请空间,然后对size和capacity(空间的大小)初始化 

3、堆打印

void HeapPrint(HP* php)
{
	for (int i = 0; i < php->size; i++)
	{
		printf("%d ", php->a[i]);
	}
	printf("\n");
}

 4、向上调整

void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;
	/*while (a[parent] < a[child])
	{
		Swap(&a[parent], &a[child]);
		child = parent;
		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;
	}
}

向上调整用于解决插入数据造成的不构成堆的问题 

5、堆插入

当我们插入一个值时,这个新插入的值也许会破坏原本的堆关系,所以我们需要进行向上调整,使其恢复堆关系。 

void HeapPush(HP* php, HPDataType x)
{
	assert(php);
	if (php->size == php->capacity)
	{
		HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType)* php->capacity * 2);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		php->a = tmp;
		php->capacity *= 2;
	}

	php->a[php->size] = x;//size代表元素个数,a[size]为下一个元素
	php->size++;

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

 6、向下调整

void AdjustDown(HPDataType* a,int n ,int parent)
{
	/*int child = a[parent * 2 + 1] > a[parent * 2 + 2] ? parent * 2 + 1 : parent * 2 + 2;
	while (child < n)
	{
		Swap(&a[parent], &a[child]);
		parent = child;
		child = a[parent * 2 + 1] > a[parent * 2 + 2] ? parent * 2 + 1 : parent * 2 + 2;
	}*/
	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;
	}
}

7、堆删除

这里选择堆顶元素和堆底元素交换,如果挪动数据会导致父子关系乱套,而这里不free最后一个元素,避免释放后继续使用。

void HeapPop(HP* php)
{
	assert(php);
	assert(!HeapEmpty(php));
	//挪动删除:1.效率低下2.关系全乱
	//只需要交换第一个元素和最后一个元素值,并php->size--
	//1.效率高2.保持父子关系
	Swap(&php->a[0], &php->a[php->size - 1]);
	php->size--;

	AdjustDown(php->a, php->size, 0);

}

 8、返回堆顶元素

HPDataType HeapTop(HP* php)
{
	assert(php);
	return php->a[0];
}

动态数组中存储的第一个元素就是堆顶元素  

9、堆判空

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

通过size的数量判空,如果为0,则堆为空,返回true 

 10、堆大小

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

 这里结构体中有size数据,返回即可。

11、堆销毁

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

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

 free掉a指针指向的空间,a指针置空,size和capacity赋值0.

三、扩展

1、堆排序(升序大堆,降序小堆)

时间复杂度O(N*logN)

先对要排序的数组建堆,然后交换堆顶元素,对剩余的元素向下调整。

void HeapSort(int* arr, int n)
{
	for (int i = (n-2)/2; i >0; i--)
	{
		AdjustDown(arr, n, i);
	}

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

		end--;
	}
}

2、topk问题

 用k个元素建小堆,剩余n-k个元素与堆顶元素比较交换即可,最后k大小的小堆中保留的是这n个数中前k个最大的数。

看到最后,如果对您有所帮助,请点赞、收藏和关注,点点关注不迷路,源码已上传Gitee有需自取:白天没有黑夜 (not-during-the-day) - Gitee.com,我们下期再见!

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

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

相关文章

SpringBoot使用ffmpeg实现视频压缩

ffmpeg简介 FFmpeg 是一个开源的跨平台多媒体处理工具集&#xff0c;用于录制、转换、编辑和流式传输音频和视频。它功能强大&#xff0c;支持几乎所有常见的音视频格式&#xff0c;是多媒体处理领域的核心工具之一。 官方文档&#xff1a;https://ffmpeg.org/documentation.h…

2025-05-31 Python深度学习9——网络模型的加载与保存

文章目录 1 使用现有网络2 修改网络结构2.1 添加新层2.2 替换现有层 3 保存网络模型3.1 完整保存3.2 参数保存&#xff08;推荐&#xff09; 4 加载网络模型4.1 加载完整模型文件4.2 加载参数文件 5 Checkpoint5.1 保存 Checkpoint5.2 加载 Checkpoint 本文环境&#xff1a; Py…

长安链起链调用合约时docker ps没有容器的原因

在调用这个命令的时候&#xff0c;发现并没有出现官方预期的合约容器&#xff0c;这是因为我们在起链的时候没有选择用docker的虚拟环境&#xff0c;实际上这不影响后续的调用&#xff0c;如果想要达到官方的效果那么你只需要在起链的时候输入yes即可&#xff0c;如图三所示

Appium+python自动化(七)- 认识Appium- 上

简介 经过前边的各项准备工作&#xff0c;终于才把appium搞定。 一、appium自我介绍 appium是一款开源的自动化测试工具&#xff0c;可以支持iOS和安卓平台上的原生的&#xff0c;基于移动浏览器的&#xff0c;混合的应用&#xff08;APP&#xff09;。 1、 使用appium进…

模块联邦:更快的微前端方式!

什么是模块联邦 在前端项目中&#xff0c;不同团队之间的业务模块可能有耦合&#xff0c;比如A团队的页面里有一个富文本模块&#xff08;组件&#xff09;&#xff0c;而B团队 的页面恰好也需要使用这个富文本模块。 传统模式下&#xff0c;B团队只能去抄A团队的代码&#x…

前端基础学习html+css+js

HTML 区块 div标签&#xff0c;块级标签 span包装小部分文本&#xff0c;行内元素 表单 CSS css选择器 css属性 特性blockinlineinline-block是否换行✅ 换行❌ 不换行❌ 不换行可设置宽高✅ 支持❌ 不支持✅ 支持常见元素div容器 p段落 h标题span文本容器 a超链接img图片…

手机打电话时将对方DTMF数字转为RFC2833发给局域网SIP坐席

手机打电话时将对方DTMF数字转为RFC2833发给局域网SIP坐席 --局域网SIP坐席呼叫 上一篇&#xff1a;手机打电话时由对方DTMF响应切换多级IVR语音菜单&#xff08;完结&#xff09; 下一篇&#xff1a;安卓App识别手机系统弹授权框包含某段文字-并自动点击确定按钮 一、前言 …

SAP Business One:无锡哲讯科技助力中小企业数字化转型的智慧之选

数字化转型&#xff0c;中小企业的必经之路 在当今竞争激烈的商业环境中&#xff0c;数字化转型已不再是大型企业的专利&#xff0c;越来越多的中小企业开始寻求高效、灵活的管理系统来优化业务流程、提升运营效率。作为全球领先的企业管理软件&#xff0c;SAP Business One…

小型语言模型:为何“小”才是“大”?

当说到人工智能&#xff08;AI&#xff09;的时候&#xff0c;大家通常会想到那些拥有数十亿参数的超大型语言模型&#xff0c;它们能做出一些令人惊叹的事情。 厉害不厉害&#xff1f;绝对厉害&#xff01; 但对于大多数企业和开发者来说&#xff0c;实用吗&#xff1f;可能…

秋招Day12 - 计算机网络 - 网络综合

从浏览器地址栏输入URL到显示网页的过程了解吗&#xff1f; 从在浏览器地址栏输入 URL 到显示网页的完整过程&#xff0c;并不是一个单一的数据包从头到尾、一次性地完成七层封装再七层解析的过程。 而是涉及到多次、针对不同目的、与不同服务器进行的、独立的网络通信交互&a…

QT-JSON

#include <QJsonDocument>#include <QJsonObject>#include <QJsonArray>#include <QFile>#include <QDebug>void createJsonFile() {// 创建一个JSON对象 键值对QJsonObject jsonObj;jsonObj["name"] "John Doe";jsonObj[…

IP 风险画像技术略解

IP 风险画像的技术定义与价值 IP 风险画像通过整合 IP 查询数据与 IP 离线库信息&#xff0c;结合机器学习算法&#xff0c;为每个 IP 地址生成多维度风险评估模型。其核心价值在于将传统的静态 IP 黑名单升级为动态风险评估体系&#xff0c;可实时识别新型网络威胁&#xff0…

秋招Day12 - 计算机网络 - 基础

说一下计算机网络体系结构 OSI七层模型&#xff0c;TCP/IP四层模型和五层体系结构 说说OSI七层模型&#xff1f; 应用层&#xff1a;最靠近用户的层&#xff0c;用于处理特定应用程序的细节&#xff0c;提供了应用程序和网络服务之间的接口。表示层&#xff1a;确保从一个系…

【网络安全】——Modbus协议详解:工业通信的“通用语言”

目录 一、初识Modbus&#xff1a;工业通信的基石 1.1 协议全称 1.2 协议简史 二、核心特性解析 2.1 架构设计 2.2 典型应用场景 三、协议族全景图 3.1 协议栈分类 3.2 版本演进对比 四、协议报文深度解析 4.1 Modbus RTU帧结构 4.2 Modbus TCP报文 五、通信机制实…

【GlobalMapper精品教程】095:如何获取无人机照片的拍摄方位角

文章目录 一、加载无人机照片二、计算方位角三、Globalmapper符号化显示方向四、arcgis符号化显示方向一、加载无人机照片 打开软件,加载无人机照片,在GLobalmapperV26中文版中,默认显示如下的航线信息。 关于航线的起止问题,可以直接从照片名称来确定。 二、计算方位角 …

小提琴图绘制-Graph prism

在 GraphPad Prism 中为小提琴图添加显著性标记(如*P<0.05)的步骤如下: 步骤1:完成统计检验 选择数据表:确保数据已按分组排列(如A列=Group1,B列=Group2)。执行统计检验: 点击工具栏 Analyze → Column analyses → Mann-Whitney test(非参数检验,适用于非正态数…

[GHCTF 2025]SQL???

打开题目在线环境&#xff1a; 先尝试注入&#xff1a; id1;show databases; 发现报错&#xff0c;后来看了wp才知道这个题目是SQLite注入。 我看的是这个师傅的wp: https://blog.csdn.net/2401_86190146/article/details/146164505?ops_request_misc%257B%2522request%255Fid…

【科研绘图系列】R语言绘制GO term 富集分析图(enrichment barplot)

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载导入数据数据预处理画图code 2code 3系统信息介绍 本文介绍了使用R语言绘制GO富集分析条形图的方法。通过加载ggplot2等R包,对GO term数据进行预处理,包括p值转换…

Laravel单元测试使用示例

Date: 2025-05-28 17:35:46 author: lijianzhan 在 Laravel 框架中&#xff0c;单元测试是一种常用的测试方法&#xff0c;它是允许你测试应用程序中的最小可测试单元&#xff0c;通常是方法或函数。Laravel 提供了内置的测试工具PHPUnit&#xff0c;实践中进行单元测试是保障代…

Kotlin委托机制使用方式和原理

目录 类委托属性委托简单的实现属性委托Kotlin标准库中提供的几个委托延迟属性LazyLazy委托参数可观察属性Observable委托vetoable委托属性储存在Map中 实践方式双击back退出Fragment/Activity传参ViewBinding和委托 类委托 类委托有点类似于Java中的代理模式 interface Base…