DS堆的实际应用(10)

文章目录

  • 前言
  • 一、堆排序
    • 建堆
    • 排序
  • 二、TopK问题
    • 原理
    • 实战
      • 创建一个有一万个数的文件
      • 读取文件并将前k个数据创建小堆
      • 用剩余的N-K个元素依次与堆顶元素来比较
      • 将前k个数据打印出来并关闭文件
    • 测试
  • 三、堆的相关习题
  • 总结


前言

  学完了堆这个数据结构的概念和特性后,我们来看看它的实际运用吧!


一、堆排序

建堆

 要学习堆排序,首先要学习堆的向下调整算法,因为要用堆排序,你首先得建堆,而建堆需要执行多次堆的向下调整算法

这是因为我们之前已经学过了,建堆时候向下调整算法时间复杂度为O(N),向上调整算法为O(NlogN)

 建堆有两种,一种是建大堆,另一种就是建小堆,假如我要升序排列,那么是要选择哪一种呢?

出人意料的是,我们选择建立大堆,这听起来很反常,但是没关系,我们后面再讲解~

 我们前面也学了,向下调整对左子树和右子树是有要求的,必须同为小堆或者同为大堆,如果我们从头开始向下调整,就显然不符合这个规律了,于是我们转换策略,从倒数第一个非叶子节点开始向下调整

至于为啥不是最后一个节点,答案是最后一个节点乃至最后一层节点都是度为0的节点,左子树右子树都是空树,也可以理解成同样类型的堆

在这里插入图片描述

//建堆
//注意i的初始值,n-1是因为对应下标要减一
//再减一除二是因为最后一个节点的父节点必是倒数第一个非叶子节点for (int i = (n - 1 - 1) / 2; i >= 0; i--){AdjustDown(php->a, php->size, i);}

排序

建完堆后,排序的思想如下:

  1. 将堆顶数据与堆的最后一个数据交换,然后对根位置进行一次堆的向下调整,但是调整时被交换到最后的那个最大的数不参与向下调整
  2. 完成步骤1后,这棵树除最后一个数之外,其余数又成一个大堆,然后又将堆顶数据与堆的最后一个数据交换,这样一来,第二大的数就被放到了倒数第二个位置上,然后该数又不参与堆的向下调整…反复执行下去,直到堆中只有一个数据时便结束。此时该序列就是一个升序

这个时候,你可能就能理解为什么升序建堆的时候使用的是大堆而不是小堆了,因为如果使用小堆,那么虽然第一个数是最小值,但是剩下的 n - 1 个数堆的结构被破坏了,得重新建堆,再选出次小,依次直到选出最大的那个数,屡次建堆带来的大量消耗是我们接受不了的

//升序 大堆
void HeapSort(int arr[], int size)
{assert(arr);//1.建堆 向上调整 O(N*logN)//for (int i = 1; i < size; i++)//{//	AdjustUp(arr, i);//}//1.向下调整建堆 O(N)//从第一个非叶子结点开始向下调整,一直到根for (int i = (size - 1 - 1) / 2; i >= 0; i--){AdjustDown(arr, size, i);}//2.向下调整 O(N*logN)int end = size - 1;//记录堆的最后一个数据的下标while (end > 0){Swap(&arr[0], &arr[end]);//将堆顶的数据和堆的最后一个数据交换AdjustDown(arr, end, 0);//对根进行一次向下调整end--;//堆的最后一个数据的下标减一}
}

二、TopK问题

原理

TOP-K问题:即求数据中前K个最大的元素或者最小的元素,一般情况下数据量都比较大

比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等

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

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

经过上述两步后,此时堆中剩余的K个元素就是所求的前K个最小或者最大的元素

其实这里思路跟堆排序大差不差,主要就是利用堆顶的特性,如果需要找出最大值,那么在小堆(都是很大的值)中堆顶就相当于门槛,至少需要被最大中的最小要大才有资格进来,然后重新筛选出来新的最小值当保安

实战

假设我们现在要找出一千万个随机数中的前k个最大的数,数据太大,内存有限,我们先考虑第一步

创建一个有一万个数的文件

 先来回顾一下以下文件函数:

//文件打开
FILE * fopen ( const char * filename, const char * mode );//前面参数为文件名 后面参数为文件打开方式
//文件关闭
int fclose ( FILE * stream );
int fprintf ( FILE * stream, const char * format, ... );//将后面函数写入的信息写入stream
int fscanf ( FILE * stream, const char * format, ... );//将stream的信息写入后面的函数

 随机数的范围为0-32767。最多只有32768个,远小于一千万,为了减少随机数的重复,我们需要将随机数加上一个数,又因为单纯的使用srand不是真正的随机数,我们还需要加上time时间戳

//1.创造随机数到文件中
void CreateNDate()
{int n = 10000000;srand((unsigned int)time(NULL));FILE* pf = fopen("data.txt", "w");//以写的方式打开文件if (pf == NULL){perror("fopen fail");return;}for (int i = 0; i < n; i++){//rand随机数有限制 只有几万个 所以+iint x = (rand() + i) % 10000000;fprintf(pf, "%d\n", x);//将数据写入文件中}fclose(pf);//关闭文件pf = NULL;//手动置空
}

读取文件并将前k个数据创建小堆

int* minheap = (int*)malloc(sizeof(int) * k);
if (minheap == NULL)
{perror("malloc fail");return;
}
//1.读取文件前k个建小堆 
for (int i = 0; i < k; i++)
{fscanf(pf, "%d", &minheap[i]);AdjustUp(minheap, i);
}

用剩余的N-K个元素依次与堆顶元素来比较

//2.读取文件的数据与堆顶数据进行比较 如果大则 向下调整
int x = 0;
while (fscanf(pf, "%d", &x) != EOF)
{if (x > minheap[0]){minheap[0] = x;AdjustDown(minheap, k, 0);}
}

将前k个数据打印出来并关闭文件

//注意这里前k个数据并不一定成排序
//但是可以肯定这是所有N个数中最大的k个数
//minheap[0]又是这k个数中最小的一个
for (int i = 0; i < k; i++)
{printf("%d ", minheap[i]);
}
free(minheap);
fclose(pf);
pf = NULL;

测试

在这里插入图片描述
 虽然我们打印出了前k大值,那我们怎么知道这个算法就是对的呢?

答案是保留原文件,修改原文件的随便一个数,使其刚好等于一千万(因为之前的数最后都会模上一千万,因此原文件中不会有数超过一千万),看下打印出来的数是否会有变化
在这里插入图片描述

此处如果需要升序或者降序打印,进行一个排序算法即可TopK方法的时间复杂度为O(NlogK)

三、堆的相关习题

一、
在这里插入图片描述
二、
在这里插入图片描述


总结

  又要开始新的篇章喽~

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

react18中实现简易增删改查useReducer搭配useContext的高级用法

useReducer和useContext前面有单独介绍过&#xff0c;上手不难&#xff0c;现在我们把这两个api结合起来使用&#xff0c;该怎么用&#xff1f;还是结合之前的简易增删改查的demo&#xff0c;熟悉vue的应该可以看出&#xff0c;useReducer类似于vuex&#xff0c;useContext类似…

wxml 模板语法-数据绑定

mustache 语法的应用场景&#xff1a; 动态绑定内容&#xff1a; 动态绑定属性&#xff1a; 三元运算&#xff1a; 算数运算&#xff1a;

MobileNet v3(相比于MobileNet v2)

概述&#xff1a; 更新Block(bneck) 使用NAS搜索参数 &#xff08;Neural Architecture Search&#xff09; 重新设计耗时层结构 更准确&#xff0c;更高效 以及表中数据展示 更新Block 1.加入SE模块 2.更新了激活函数 首先通过一个1*1的卷积层来进行一个升维处理&#…

深入了解vcruntime140.dll:为什么会出现vcruntime140.dll丢失及修复

“找不到vcruntime140.dll”或“vcruntime140.dll丢失”是什么情况&#xff1f;出现这样的情况有什么方法可以解决&#xff1f;今天这篇文章将和大家聊聊vcruntime140.dll丢失错误的详细解决办法&#xff0c;一步步教大家修复错误vcruntime140.dll文件。 一步步修复错误vcrunti…

[含文档+PPT+源码等]精品基于springboot实现的原生微信小程序酒店管理系统[包运行成功+永久免费答疑辅导]

基于Spring Boot实现的原生微信小程序酒店管理系统&#xff0c;其背景主要源于酒店行业的信息化需求和移动互联网技术的快速发展。以下是对该背景的具体阐述&#xff1a; 一、酒店行业的信息化需求 信息量剧增&#xff1a; 随着旅游业的蓬勃发展&#xff0c;酒店数量不断增加&…

AI金融攻防赛:金融场景凭证篡改检测(DataWhale组队学习)

引言 大家好&#xff0c;我是GISer Liu&#x1f601;&#xff0c;一名热爱AI技术的GIS开发者。本系列文章是我跟随DataWhale 2024年10月学习赛的AI金融攻防赛学习总结文档。本文主要讲解如何解决 金融场景凭证篡改检测的核心问题&#xff0c;以及解决思路和代码实现过程。希望…

【图解版】力扣第1题:两数之和

Golang代码实现 func twoSum(nums []int, target int) []int {m : make(map[int]int)for i : range nums {if _, ok : m[target - nums[i]]; ok {return []int{i, m[target - nums[i]]}} m[nums[i]] i}return nil }

Java语言-抽象类

目录 1.抽象类概念 2.抽象类语法 3.抽象类特性 4.抽象类作用 1.抽象类概念 在面向对象的概念中&#xff0c;所有的对象都是通过类来描绘的&#xff0c;但是反过来&#xff0c;并不是所有的类都是用来描绘对象的&#xff0c; 如果 一个类中没有包含足够的信息来描绘一个具体…

本地群晖NAS安装phpMyAdmin管理MySQ数据库实战指南

文章目录 前言1. 安装MySQL2. 安装phpMyAdmin3. 修改User表4. 本地测试连接MySQL5. 安装cpolar内网穿透6. 配置MySQL公网访问地址7. 配置MySQL固定公网地址8. 配置phpMyAdmin公网地址9. 配置phpmyadmin固定公网地址 前言 本文主要介绍如何在群晖NAS安装MySQL与数据库管理软件p…

Excel:vba实现合并工作表(表头相同)

这个代码应该也适用于一些表头相同的工作表的汇总&#xff0c;只需要修改想要遍历的表&#xff0c;适用于处理大量表头相同的表的合并 这里的汇总合并表 total 是我事先创建的&#xff0c;我觉得比用vba代码创建要容易一下&#xff0c;如果不事先创建汇总表就用下面的代码&…

多态对象的存储方案小结

某个类型有几种不同的子类&#xff0c;Jackson中的JsonTypeInfo 和JsonSubTypes可以应对这种情形&#xff0c;但有点麻烦&#xff0c;并且name属性必须是字符串、必须用Jackson为基础的json工具类对json字符串和对象进行序列化和反序列化。用过一次这种方案后边就不想再用了。 …

[Linux] 逐层深入理解文件系统 (1)—— 进程操作文件

标题&#xff1a;[Linux] 文件系统 &#xff08;1&#xff09;—— 进程操作文件 个人主页水墨不写bug &#xff08;图片来源于网络&#xff09; 目录 一、进程与打开的文件 二、文件的系统调用与库函数的关系 1.系统调用open() 三、内存中的文件描述符表 四、缓冲区…

python实现录屏功能

python实现录屏功能 将生成的avi文件转为mp4格式后删掉avi文件 参考感谢&#xff1a;https://www.cnblogs.com/peachh/p/16549254.html import os import cv2 import time import threading import numpy as np from PIL import ImageGrab from pynput import keyboard from da…

前海湾地铁A出口临时路边免费停车点探寻

​ ​我们公司不少同事下班直接从桂湾地铁步行到前海湾地铁A出口&#xff0c;很多人也是一样&#xff0c;可能是上下班高峰期停车还不如步行快&#xff1f;由于这条路的很多高楼大厦还没建好&#xff0c;一路过来可以看到不少路边停车点。以前海湾地铁A出口为例&#xff0c;…

数学建模算法与应用 第5章 插值与拟合方法

目录 5.1 插值方法 Matlab代码示例&#xff1a;线性插值 Matlab代码示例&#xff1a;样条插值 5.2 曲线拟合的线性最小二乘法 Matlab代码示例&#xff1a;线性拟合 5.3 最小二乘优化与多项式拟合 Matlab代码示例&#xff1a;多项式拟合 5.4 曲线拟合与函数逼近 Matlab代…

基于SSM的个性化商铺系统【附源码】

基于SSM的个性化商铺系统 效果如下&#xff1a; 用户登录界面 app首页界面 商品信息界面 店铺信息界面 用户功能界面 我的订单界面 后台登录界面 管理员功能界面 用户管理界面 商家管理界面 店铺信息管理界面 商家功能界面 个人中心界面 研究背景 研究背景 科学技术日新月异…

10 分钟使用豆包 MarsCode 帮我搭建一套后台管理系统

以下是「 豆包MarsCode 体验官」优秀文章&#xff0c;作者把梦想揉碎。 十分钟使用豆包 MarsCode 搭建后台管理项目 在这个快节奏的时代&#xff0c;开发者们总是希望能够快速、高效地完成项目的搭建与开发工作。无论是初创企业还是大型公司&#xff0c;后台管理系统都是必不可…

Redis --- 第四讲 --- 常用数据结构 --- 其他类型stream、bitmap……。补充内容scan命令。

通过前面的学习&#xff0c;我们已经学习了Redis最关键的五个数据结构&#xff1a;String、List、Hash、Set、ZSet。这五个数据结构应用广泛&#xff0c;频繁使用。 redis中包含的所有类型&#xff0c;下面将要介绍不常用的类型。 一、streams类型介绍 事件、epoll/IO多路复…

力扣56~60题

题56&#xff08;中等&#xff09;&#xff1a; 分析&#xff1a; 显然可以贪心也可以动态规划 python代码&#xff1a; class Solution:def merge(self, intervals: List[List[int]]) -> List[List[int]]:s_intervalssorted(intervals,keySolution.sort_rule)res[]start…

深度解析LMS(Least Mean Squares)算法

目录 一、引言二、LMS算法简介三、LMS算法的工作原理四、LMS算法的特点五、LMS算法的应用场景六、LMS算法的局限性七、总结八、进一步探讨 一、引言 自适应滤波器是一种动态调整其参数以适应变化环境的信号处理工具&#xff0c;广泛应用于噪声消除、信道均衡和系统识别等领域。…