数据结构第十二弹---堆的应用

堆的应用

  • 1、堆排序
  • 2、TopK问题
  • 3、堆的相关习题
  • 总结

1、堆排序

要学习堆排序,首先要学习堆的向下调整算法,因为要用堆排序,你首先得建堆,而建堆需要执行多次堆的向下调整算法。
但是,使用向下调整算法需要满足一个前提:
 若想将其调整为小堆,那么根结点的左右子树必须都为小堆。
 若想将其调整为大堆,那么根结点的左右子树必须都为大堆。

在这里插入图片描述

向下调整算法的基本思想(大堆):
 1.从根结点处开始,选出左右孩子中值较大的孩子。
 2.让大的孩子与其父亲进行比较。  若大的孩子比父亲还大,则该孩子与其父亲的位置进行交换。并将原来大的孩子的位置当成父亲继续向下进行调整,直到调整到叶子结点为止。
 若大的孩子比父亲小,则不需处理了,调整完成,整个树已经是大堆了。

在这里插入图片描述
使用堆的向下调整算法需要满足其根结点的左右子树均为大堆或是小堆才行,那么如何才能将一个任意树调整为堆呢?

答案很简单,我们只需要从倒数第一个非叶子结点开始,从后往前,按下标,依次作为根去向下调整即可。

在这里插入图片描述
建堆代码

//建堆for (int i = (n - 1 - 1) / 2; i >= 0; i--){AdjustDown(php->a, php->size, i);}

根据上一弹堆的向下调整算法时间复杂度计算可知,建堆的时间复杂度为O(N)。
那么堆建好后,如何进行堆排序呢?
步骤如下:

1、将堆顶数据与堆的最后一个数据交换,然后对根位置进行一次堆的向下调整,但是调整时被交换到最后的那个最大的数不参与向下调整。
2、完成步骤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--;//堆的最后一个数据的下标减一}
}

2、TopK问题

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

1. 用数据集合中前K个元素来建堆

前k个最大的元素,则建小堆
前k个最小的元素,则建大堆

2. 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素

将剩余N-K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素。

假设我们要找出10亿个随机数中的前k个最大的数。
假设数据类型为int,那么占用的内存是多少?
1GB=1024MB
1MB=1024Kb
1KB=1024Byte
10亿个数则是10亿字节,40亿Byte=3,906,250KB=3,814.697MB=3.725GB
因为内存的空间是有限的,所以在处理这么大内存的数据时,我们需要用到文件处理。

为了让速度较快一些,我们就假设在一千万个随机数中求前K个数。

1、我们需要创建一个有一万个数的文件。

此处我们需要用到两个文件处理函数和文件打开关闭函数。

//文件打开
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的信息写入后面的函数

创建随机值,需要用到srand(),但是随机数的范围为0-32767。最多只有32768个,远小于一千万,为了减少随机输的重复,我们需要将随机数加上一个数。单纯的使用srand不是真正的随机时,这些我们需要用到时间这个参数,使它为真正的随机数。srand的头文件是#include<stdlib.h>。time的头文件是#include<time.h>。

srand((unsigned int)time(NULL));

代码实现

//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;//手动置空
}

测试
在这里插入图片描述
在这里插入图片描述

2、 用数据集合中前K个元素来建堆,用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素

此处找最大的前k个,建小堆。
建小堆大的数据才能进来,最后留下的也是大的数据。
建大堆则只能进来小的数据,不符合题意。

2.1、打开文件

//打开文件
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{perror("fopen error");return;
}

2.2、读取文件并将前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);
}

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

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

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

for (int i = 0; i < k; i++)
{printf("%d ", minheap[i]);
}
free(minheap);
fclose(pf);
pf = NULL;

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

此处博主的解决办法是修改文件中的k个值,改为都是超过一千万的值,如果打印出来的K个值是修改的值,那么此算法就是正确的。

在这里插入图片描述
经过修改打印出来的就是修改的值,说明算法没有问题。此处如果需要升序或者降序打印,进行一个排序算法即可。

3、堆的相关习题

1.下列关键字序列为堆的是:()
A 100,60,70,50,32,65
B 60,70,65,50,32,100
C 65,100,70,32,50,60
D 70,65,100,32,50,60
E 32,50,100,70,65,60
F 50,100,70,65,60,32

在这里插入图片描述

2.已知小根堆为8,15,10,21,34,16,12,删除关键字 8 之后需重建堆,在此过程中,关键字之间的比较次
数是()。
A 1
B 2
C 3
D 4

在这里插入图片描述

3.一组记录排序码为(5 11 7 2 3 17),则利用堆排序方法建立的初始堆为
A(11 5 7 2 3 17)
B(11 5 7 2 17 3)
C(17 11 7 2 3 5)
D(17 11 7 5 3 2)
E(17 7 11 3 5 2)
F(17 7 11 3 2 5)

在这里插入图片描述

4.最小堆[0,3,2,5,7,4,6,8],在删除堆顶元素0之后,其结果是()
A[3,2,5,7,4,6,8]
B[2,3,5,7,4,6,8]
C[2,3,4,5,7,8,6]
D[2,3,4,5,6,7,8]

在这里插入图片描述

总结

本篇博客就结束啦,谢谢大家的观看,如果公主少年们有好的建议可以留言喔,谢谢大家啦!

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

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

相关文章

面试算法110:所有路径

题目 一个有向无环图由n个节点&#xff08;标号从0到n-1&#xff0c;n≥2&#xff09;组成&#xff0c;请找出从节点0到节点n-1的所有路径。图用一个数组graph表示&#xff0c;数组的graph[i]包含所有从节点i能直接到达的节点。例如&#xff0c;输入数组graph为[[1&#xff0c…

微信小程序Canvas画布绘制图片、文字、矩形、(椭)圆、直线

获取CanvasRenderingContext2D 对象 .js onReady() {const query = wx.createSelectorQuery()query.select(#myCanvas).fields({ node: true, size: true }).exec((res) => {const canvas = res[0].nodeconst ctx = canvas.getContext(2d)canvas.width = res[0].width * d…

2024--Django平台开发-Web框架和Django基础(二)---Mysql多版本共存(Mac系统)

MySQL多版本共存&#xff08;Mac系统&#xff09; 想要在Mac系统上同时安装【MySQL5.7 】【MySQL8.0】版本&#xff0c;需要进行如下的操作和配置。 想要同时安装两个版本可以采取如下方案&#xff1a; 方案1&#xff1a;【讲解】 MySQL57&#xff0c;用安装包进行安装。 MyS…

遗传算法解决函数最大化问题的完整Python实现

遗传算法&#xff08;Genetic Algorithm&#xff0c;简称GA&#xff09;是一种模拟生物进化过程的启发式优化算法。它通过模拟自然选择、交叉和变异等基因操作&#xff0c;来搜索问题的最优解。 遗传算法的基本思想是通过模拟生物的遗传机制来搜索解空间。算法维护一个种群&am…

每日一篇英语文章分享:I have a dream. 争取早日阅读论文自由.

我有一个梦想》&#xff08;英文&#xff1a;I have a dream&#xff09;是美国黑人民权运动领袖马丁路德金于1963年8月28日在华盛顿林肯纪念堂发表的纪念性演讲。 《我有一个梦想》是马丁路德金在美国黑人受种族歧视和迫害由来已久的背景下&#xff0c;为了推动美国国内黑人争…

【语义解析:连接自然语言与机器智能的桥梁】

语义解析&#xff1a;连接自然语言与机器智能的桥梁 语义解析技术可以提高人机交互的效率和准确性&#xff0c;在自然语言处理、数据分析、智能客服、智能家居等领域都有广泛的应用前景。特别是在大数据时代&#xff0c;语义解析能够帮助企业更快速地从大量的数据中获取有用的…

龍运当头--html做一个中国火龙祝大家龙年大吉

🐉效果展示 🐉HTML展示 <body> <!-- partial:index.partial.html --> <svg><defs><g id=

<Python>PyQt5中UI界面和逻辑函数分开写的一种方式

前言 如果经常使用PyQt5这种模块来编写带UI界面的程序&#xff0c;那么很自然的就会涉及到&#xff0c;一旦程序比较大&#xff0c;UI控件多的时候&#xff0c;需要将UI和逻辑程序分离&#xff0c;这样方便管理&#xff0c;也方便维护。 配置&#xff1a; 平台&#xff1a;win…

【算法Hot100系列】搜索旋转排序数组

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老导航 檀越剑指大厂系列:全面总结 jav…

web缓存之nginx缓存

一、nginx缓存知识 网络缓存位于客户端和 "源服务器 "之间&#xff0c;保存着所有可见内容的副本。当客户端请求缓存中存储的内容时&#xff0c;它可以直接从缓存中检索内容&#xff0c;而无需与服务器通信。这样&#xff0c;网络缓存就 "接近 "了客户端&a…

在线双目测宽仪 板材实时监测和数据分析!

在各种板材类生产领域里&#xff0c;在线品质检测技术都是非常重要的&#xff0c;它很大程度上决定了生产的质量。在线双目测宽仪就是当前很受欢迎的一种宽度在线检测设备&#xff0c;它采用了双目视觉检测技术&#xff0c;实现宽度尺寸的在线检测&#xff0c;功能十分强大&…

java物品检验管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 java Web 物品检验管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysq…

手把手教你如何用python进行数据分析!(附四个案例)

一、前期准备 三个包&#xff1a;Numpy、Pandas和matplotlib&#xff1b;工具&#xff1a;jupyter notebook。首先确保导入这两个包 #导入Numpy包 import numpy as np #导入Pandas包 import pandas as pd二、基础知识 Pandas有三种数据结构&#xff1a;Series、DataFrame和P…

2024 CSRankings全球计算机科学排名发布!清华AI第一,哈工大NLP第一

大家好我是二狗。 这两天全球计算机科学排名 CSRankings 2024发布啦&#xff01; 下面二狗就带大家来看一下最新的排名情况。 清华、北大、上海交大AI领域霸榜前三 在AI板块&#xff0c;主要有人工智能、计算机视觉、机器学习、自然语言处理、网络&信息检索5个细分领域。…

[Docker] Mac M1系列芯片上完美运行Docker

docker pull qinchz/dm8-arm64 container_name: dm8ports:- "5236:5236"mem_limit: 1gmemswap_limit: 1gvolumes:- /data/dm8:/home/dmdba/data 数据库实例参数已修改&#xff0c;接近oracle使用习惯 #字符集 utf-8 CHARSET1 #VARCHAR 类型对象的长度以字符为单位 …

外包做了5个月,技术退步一大半了。。。

先说一下自己的情况&#xff0c;本科生&#xff0c;20年通过校招进入深圳某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…

什么是Helm?它是如何提升云原生应用私有化部署效率的

转载至我的博客 &#xff0c;公众号&#xff1a;架构成长指南 试想一下&#xff0c;如果有一个项目有50 个微服务&#xff0c;每个微服务都有service、deployment、ingress、pvc等 yaml 文件&#xff0c;算下来大概有 200 个文件&#xff0c;然后这个项目需要基于k8s进行私有化…

动态规划python简单例子-斐波那契数列

def fibonacci(n):dp [0, 1] [0] * (n - 1) # 初始化动态规划数组for i in range(2, n 1):dp[i] dp[i - 1] dp[i - 2] # 计算斐波那契数列的第 i 项print(dp)return dp[n] # 返回斐波那契数列的第 n 项# 示例用法 n 10 # 计算斐波那契数列的第 10 项 result fibonac…

Adding Conditional Control to Text-to-Image Diffusion Models——【论文笔记】

本文发表于ICCV2023 论文地址&#xff1a;ICCV 2023 Open Access Repository (thecvf.com) 官方实现代码&#xff1a;lllyasviel/ControlNet: Let us control diffusion models! (github.com) Abstract 论文提出了一种神经网络架构ControlNet,可以将空间条件控制添加到大型…

【Linux】进程

----------------| 本文目录 |---------------- 1. 进程1.1 基本概念1.2 描述进程 - PCB1.2.1 task_struct - PCB的一种1.2.2 task_struct 内容分类 1.3 组织进程1.4 查看进程1.5 通过系统调用获取进程标示符1.6 通过系统调用创建进程 - fork初识 2. 进程状态2.1 看看Linux内核…