当前位置: 首页 > news >正文

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

目录

堆的概念及结构

​编辑

堆的实现 

实现堆的接口

堆的初始化

堆的打印

堆的销毁

获取最顶的根数据

 交换

堆的插入(插入最后)

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

堆的删除(删除根)

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

堆排序

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-1for (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个最大的数据

相关文章:

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

目录 堆的概念及结构 ​编辑 堆的实现 实现堆的接口 堆的初始化 堆的打印 堆的销毁 获取最顶的根数据 交换 堆的插入&#xff08;插入最后&#xff09; 向上调整&#xff08;这次用的是小堆&#xff09; 堆的删除&#xff08;删除根&#xff09; 向下调整&#xff08;这次用的…...

保护数字前沿:下一代防火墙如何塑造网络安全的未来

下一代防火墙通过提供先进的威胁检测、精细控制和云安全功能&#xff0c;正在重塑网络安全的未来。随着数字环境的不断发展&#xff0c;组织必须采用这些创新解决方案来保护其数字资产并维护安全的数字前沿。 在当今互联的世界中&#xff0c;网络威胁变得越来越复杂&#xff0c…...

深入理解Java中的String.join方法

在 Java 编程中&#xff0c;字符串操作是非常常见的需求。在 Java 8 中引入了一个方便的字符串连接方法 String.join&#xff0c;它能够简洁而高效地将多个字符串连接起来。本篇博客将深入介绍 String.join 方法的使用和原理。 什么是String.join方法&#xff1f; String.join…...

【MySQL系列】 第三章 · 函数

写在前面 Hello大家好&#xff0c; 我是【麟-小白】&#xff0c;一位软件工程专业的学生&#xff0c;喜好计算机知识。希望大家能够一起学习进步呀&#xff01;本人是一名在读大学生&#xff0c;专业水平有限&#xff0c;如发现错误或不足之处&#xff0c;请多多指正&#xff0…...

微信小程序wxss定位/选择/查找元素的几种方式

wxss定位、选择、查找元素的几种方式与css类似&#xff0c;下面介绍常用的几种&#xff1a; 选择器样例样例描述.class.intro选择所有拥有 class"intro" 的组件#id#firstname选择拥有 id"firstname" 的组件elementview选择所有 view 组件element, element…...

Canvas—从入门到案例实现

文章目录 Canvas—从入门到案例实现一、设置canvas环境1.1 <canvas>元素1.2 渲染上下文context 二、形状与路径的绘制2.1 形状绘制2.2 路径绘制2.3 绘制一个笑脸 三、使用样式和颜色四、绘制文本五、使用图像5.1 图片源5.2 获取页面内的图片5.3 缩放Scaling5.4 切片Slici…...

飞书开发学习笔记(六)-网页应用免登

飞书开发学习笔记(六)-网页应用免登 一.上一例的问题修正 在上一例中&#xff0c;飞书登录查看网页的界面显示是有误的&#xff0c;看了代码&#xff0c;理论上登录成功之后&#xff0c;应该显示用户名等信息。 最后的res.nickName是用户名&#xff0c;res.i18nName.en_us是英…...

【ROS】Nav2源码下载、编译、运行

【ROS】郭老二博文之:ROS目录 1、源码下载 1.1 源码地址 https://github.com/ros-planning/navigation2 1.2 创建工程目录 ROS2使用目录结果来管理项目,因此在下载前需要创建好目录结构: mkdir -p ~/git/nav2/src1.3 下载 git中默认版本是main。本人的开发环境为Ubun…...

微信小程序 30分钟倒计时功能

ps:凑个数 getTimeDiff(date) {let _this = this;let curTime = new Date(date)_this.countDown(_this.timeFormatConvert(new Date(curTime.setMinutes(curTime.getMinutes() + 30))))},timeFormatConvert(e) {const Y = e.getFullYear(); // 年const M = this.prefixZero(e.…...

JAVA判断指定日期是否在指定的时间段内

参考文献: Java语言判断当前时间在时间范围内_java判断时间区间-CSDN博客 package com.itheima.method2;import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date;public class DateTest {public static voi…...

关于晋升与跳槽的一些思考

内部晋升 内部晋升是我优先考虑的&#xff0c;原因有很多。首先这是一个新业务&#xff0c;相对而言容易拿到结果。其次我想体验不同的晋升路径&#xff0c;内部晋升答辩&#xff0c;是挑战也是一次成长的机会&#xff0c;是一次他人帮助自己review的机会。作为从校园出来的校…...

url找不到404的问题,url被拼接

今天遇到一个测试feign调用的功能&#xff0c;如图所示 先说结论 Controller换成RestController 将日志设置为debug模式 被DispatcherServlet FORWARD了 找到路径 对属性设置断点&#xff0c;看下是哪注进来的 我们再去找encodedPath 此处是undertow的源码&#xff0c;但是und…...

如何解决golang开发中遇到的报错:checksum mismatch downloaded

问题描述 如题&#xff0c;项目开发中遇到如下报错&#xff08;你的报错信息可能与我的有一点区别&#xff0c;如verifying的包名&#xff0c;但是问题本质都是一样的&#xff09;&#xff1a; verifying github.com/algorand/go-codec/codecv1.1.8/go.mod: checksum mismatc…...

4.以docker容器生成镜像推送到阿里云镜像仓库

1.开通阿里云镜像仓库 1.1 登录阿里云&#xff0c;访问容器镜像服务。地址如下&#xff1a; https://cr.console.aliyun.com/cn-shanghai/instances 1.2 个人学习为例&#xff0c;创建个人版实例 1.2.1 点击个人实例 1.2.2 .创建个人实例 1.2.3 创建完成后&#xff0c;设置…...

CSS Form表单布局

效果图 <Tab IsCard"true"><TabItem Text"表单信息-DIV版本"><div class"row"><div class"col"><label for"field1">工程名称:</label><input class"form-control" type&…...

c++ shared_mutex 读写锁使用详解

c 读写锁使用详解 std::shared_mutex c17 头文件 #include <shared_mutex>。用于实现共享和独占访问的互斥锁。提供了一种更加灵活的机制&#xff0c;允许多个线程在共享模式下读取数据&#xff0c;但只允许单个线程在独占模式下写入或修改数据。与 std::mutex 相比&am…...

淘宝商品详情接口,淘宝详情页接口,宝贝详情页接口,商品属性接口,商品信息查询,商品详细信息接口,h5详情,淘宝API接口演示案例

淘宝详情接口API可以帮助简化运营流程&#xff0c;更加专注于产品本身。通过调用API&#xff0c;可以快速获取到商品的标题、图片、价格等信息&#xff0c;省去了手动编写和编辑的繁琐过程。这样就可以更快地上架新品、更新商品信息&#xff0c;提高运营的效率。 taobao.item_…...

python爬取网站数据,作为后端数据

一. 内容简介 python爬取网站数据&#xff0c;作为后端数据 二. 软件环境 2.1vsCode 2.2Anaconda version: conda 22.9.0 2.3代码 链接&#xff1a; 三.主要流程 3.1 通过urllib请求网站 里面用的所有的包 ! pip install lxml ! pip install selenium ! pip install…...

【机器学习】K近邻算法:原理、实例应用(红酒分类预测)

案例简介&#xff1a;有178个红酒样本&#xff0c;每一款红酒含有13项特征参数&#xff0c;如镁、脯氨酸含量&#xff0c;红酒根据这些特征参数被分成3类。要求是任意输入一组红酒的特征参数&#xff0c;模型需预测出该红酒属于哪一类。 1. K近邻算法介绍 1.1 算法原理 原理&a…...

基于安卓android微信小程序的快递取件及上门服务系统

项目介绍 本文从管理员、用户的功能要求出发&#xff0c;快递取件及上门服务中的功能模块主要是实现管理员服务端&#xff1b;首页、个人中心、用户管理、快递下单管理、预约管理、管理员管理、系统管理、订单管理&#xff0c;用户客户端&#xff1b;首页、快递下单、预约管理…...

Tftpd32/Tftpd64不止是TFTP!手把手教你玩转它的DHCP和Syslog服务器功能

Tftpd32/Tftpd64&#xff1a;解锁DHCP与Syslog服务的隐藏潜力当大多数人提起Tftpd32/Tftpd64时&#xff0c;第一反应往往是它作为TFTP服务器的功能。这款轻量级工具确实在文件传输领域表现出色&#xff0c;但它的能力远不止于此。今天&#xff0c;我们将深入探索这款软件中两个…...

用STM32CubeMX和HAL库快速上手WS2812B:告别手动计算延时,一键生成驱动框架

基于STM32CubeMX的WS2812B智能灯光控制&#xff1a;从零构建现代化驱动方案在智能硬件和物联网设备快速发展的今天&#xff0c;WS2812B可编程LED灯带因其丰富的色彩表现和简单的单线控制方式&#xff0c;成为创客和工程师们最喜爱的显示组件之一。然而&#xff0c;传统的寄存器…...

嵌入式快速原型开发:基于Sceptre平台与LPC2148的实战指南

1. 项目概述&#xff1a;Sceptre&#xff0c;一个被低估的嵌入式快速原型利器 在嵌入式开发的世界里&#xff0c;我们总是在寻找那个“刚刚好”的平台&#xff1a;它要足够强大&#xff0c;能跑复杂的算法&#xff1b;要足够小巧&#xff0c;能塞进各种外壳&#xff1b;要足够便…...

别再死记硬背了!用5个生活化比喻彻底搞懂Linux进程的fork、exec和wait

别再死记硬背了&#xff01;用5个生活化比喻彻底搞懂Linux进程的fork、exec和wait想象你正在厨房准备一顿大餐。菜谱上写着"切菜"、"炒菜"、"装盘"等步骤&#xff0c;但突然发现需要同时处理多道菜品——这时候&#xff0c;你会本能地让家人分工…...

Adobe-GenP 3.0:轻松激活Adobe全家桶的完整指南

Adobe-GenP 3.0&#xff1a;轻松激活Adobe全家桶的完整指南 【免费下载链接】Adobe-GenP Adobe CC 2019/2020/2021/2022/2023 GenP Universal Patch 3.0 项目地址: https://gitcode.com/gh_mirrors/ad/Adobe-GenP Adobe-GenP 3.0是一款专为Adobe Creative Cloud系列软件…...

HiveWE终极指南:快速掌握魔兽争霸III现代化地图编辑器

HiveWE终极指南&#xff1a;快速掌握魔兽争霸III现代化地图编辑器 【免费下载链接】HiveWE A Warcraft III world editor. 项目地址: https://gitcode.com/gh_mirrors/hi/HiveWE 还在为传统魔兽争霸III地图编辑器缓慢的加载速度和复杂的操作界面而烦恼吗&#xff1f;Hiv…...

LoRa物联网与动态基线算法在养殖体温监测中的实战应用

1. 项目概述&#xff1a;为什么我们需要一个智能体温监测系统&#xff1f;在规模化养殖场里干了十几年&#xff0c;我见过太多因为体温异常没被及时发现而导致的损失。一头育肥猪突然不吃食&#xff0c;等饲养员第二天巡栏发现时&#xff0c;可能已经高烧好几天&#xff0c;继发…...

【Veo 2提示词SOP白皮书】:从模糊意图到像素级输出的8步标准化工作流(附NASA级测试用例库)

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;Veo 2提示词工程的本质与范式跃迁 Veo 2并非单纯升级的视频生成模型&#xff0c;而是一次提示词工程范式的根本性重构——它将传统“指令式提示”&#xff08;prompt-as-command&#xff09;转向“意图…...

Hermes Agent 框架如何对接 Taotoken 作为自定义模型供应商并配置环境变量

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 Hermes Agent 框架如何对接 Taotoken 作为自定义模型供应商并配置环境变量 Hermes Agent 是一个流行的 AI 代理开发框架&#xff0…...

Godot 2D随机地图三大静默故障:黑屏、穿墙、寻路失败的根源与修复

1. 为什么刚上手Godot做2D随机地图就总卡在“生成出来是黑的”“角色穿墙”“房间连不通”这三件事上&#xff1f;如果你是刚从Unity或GameMaker转来Godot&#xff0c;或者第一次用GDScript写程序逻辑的新手&#xff0c;大概率已经在2D随机地图生成这个环节反复摔过跟头——不是…...