DS堆的实际应用(10)

news2025/2/23 18:02:22

文章目录

  • 前言
  • 一、堆排序
    • 建堆
    • 排序
  • 二、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随机数有限制 只有几万个 所以+i
		int 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.coloradmin.cn/o/2217361.html

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

相关文章

限时设计ui

ctrl-------放大缩小 空格-----画面移动 alt------复制 页面<画板<图层 添加交互事件 原型 点击蓝色的圆&#xff0c;从1跳转到2 点击绿色的圆&#xff0c;从2跳转到1

基于SSM+Vue+MySQL的健身房管理系统

系统展示 系统背景 随着人们生活水平的提高和健康意识的增强&#xff0c;越来越多的人选择去健身房锻炼。传统的健身房管理方式往往依赖于纸质记录和人工操作&#xff0c;这种方式不仅效率低下&#xff0c;而且容易出错。为了提高健身房的管理效率和服务质量&#xff0c;开发一…

python项目实战——下载美女图片

python项目实战——下载美女图片 文章目录 python项目实战——下载美女图片完整代码思路整理实现过程使用xpath语法找图片的链接检查链接是否正确下载图片创建文件夹获取一组图片的链接获取页数 获取目录页的链接 完善代码注意事项 完整代码 import requests import re import…

图文检索综述(2):Deep Multimodal Data Fusion

Deep Multimodal Data Fusion 摘要1 引言2 基于编码器-解码器融合2.1 数据级别融合2.2 分层特征融合2.3 决策级别融合 3 基于注意力融合3.1 模态内的自注意力3.2 模态间的交叉注意力3.3 基于transformer的方法 4 基于图神经网络融合4.1 单个模态的表示学习4.2 融合数据的表示学…

【数据结构】宜宾大学-计院-实验三

线性表的应用——实现两多项式的相加 课前准备&#xff1a;实验学时&#xff1a;2实验目的&#xff1a;实验内容&#xff1a;实验结果&#xff1a;实验报告:&#xff08;及时撰写实验报告&#xff09;实验测试结果&#xff1a;代码实现&#xff1a;&#xff08;C/C&#xff09;…

Java 小游戏《超级马里奥》

文章目录 一、效果展示二、代码编写1. 素材准备2. 创建窗口类3. 创建常量类4. 创建动作类5. 创建关卡类6. 创建障碍物类7. 创建马里奥类8. 编写程序入口 一、效果展示 二、代码编写 1. 素材准备 首先创建一个基本的 java 项目&#xff0c;并将本游戏需要用到的图片素材 image…

华为 HCIP-Datacom H12-821 题库 (38)

&#x1f423;博客最下方微信公众号回复题库,领取题库和教学资源 &#x1f424;诚挚欢迎IT交流有兴趣的公众号回复交流群 &#x1f998;公众号会持续更新网络小知识&#x1f63c; 1.请对 2001:0DB8:0000:C030:0000:0000:09A0:CDEF 地址进行压缩。&#xff08; &#xff09;&…

阻塞I/O与非阻塞I/O

目录 一、基本概念 二、阻塞I/O的实现机制 —— 等待队列 一、基本概念 阻塞&#xff1a;在执行单元进行操作时&#xff0c;如果不能获得申请的资源&#xff0c;则执行单元挂起直至资源可用后再进行操作。 非阻塞&#xff1a;在执行单元进行操作时&#xff0c;如果不能获得申…

UDP反射放大攻击防范手册

UDP反射放大攻击是一种极具破坏力的恶意攻击手段。 一、UDP反射放大攻击的原理 UDP反射放大攻击主要利用了UDP协议的特性。攻击者会向互联网上大量的开放UDP服务的服务器发送伪造的请求数据包。这些请求数据包的源IP地址被篡改为目标受害者的IP地址。当服务器收到这些请求后&…

爬虫实战(黑马论坛)

1.定位爬取位置内容&#xff1a; # -*- coding: utf-8 -*- import requests import time import re# 请求的 URL 和头信息 url https://bbs.itheima.com/forum-425-1.html headers {user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like…

DBSwitch和Seatunel

一、DBSwitch 什么是DBSwitch?它主要用在什么场景&#xff1f; 通过步骤分析可以看到这个是通过配置数据源&#xff0c;采用一次性或定时方案&#xff0c;同步到数据仓库的指定表&#xff0c;并且指定映射关系的工具。有点类似于flinkcdc的增量同步。 参考&#xff1a; dbs…

【实战案例】SpringBoot项目中异常处理通用解决方案

项目中经常会出现一些异常&#xff0c;比如在新增项目的时候必要的字段没有填写。在springboot项目中&#xff0c;遇到异常会往上抛出给调用方&#xff0c;DAO层遇到异常抛给Service层&#xff0c;Service层遇到异常抛给Controller层&#xff0c;Controller层遇到异常就抛给了S…

Qt-系统网络HTTP客户端(66)

目录 描述 相关函数 使用 准备工作 处理响应 测试 代码 补充 描述 进⾏ Qt 开发时, 和服务器之间的通信很多时候也会⽤到 HTTP 协议 Qt 中提供了客户端&#xff0c;但是并没有提供相应的服务器的库&#xff0c;所以这里我们只讨论 客户端 • 通过 HTTP 从服务器获取…

Unity 2d UI 实时跟随场景3d物体

2d UI 实时跟随场景3d物体位置&#xff0c;显示 3d 物体头顶信息&#xff0c;看起来像是场景中的3dUI&#xff0c;实质是2d UIusing System.Collections; using System.Collections.Generic; using UnityEngine; using DG.Tweening; using UnityEngine.UI; /// <summary>…

RequestBody接收参数报错com.fasterxml.jackson.databind.exc.MismatchedInputException

目录&#xff1a; 1、错误现象2、解决办法3、最终验证 1、错误现象 报错的现象和代码如下&#xff1a; 2、解决办法 查了很多都说参数类型对不上&#xff0c;但是明明是对上的&#xff0c;没有问题&#xff0c;最后只有换接收方式后验证是可以的&#xff1b;最终想了一下&…

Flink状态一致性保证

前言 一个Flink作业由一系列算子构成&#xff0c;每个算子可以有多个并行实例&#xff0c;这些实例被称为 subTask&#xff0c;每个subTask运行在不同的进程或物理机上&#xff0c;以实现作业的并行处理。在这个复杂的分布式场景中&#xff0c;任何一个节点故障都有可能导致 F…

智能算力中心万卡GPU集群架构深度解析

智能算力中心万卡GPU集群架构深度分析 自ChatGPT发布&#xff0c;科技界大模型竞赛如火如荼。数据成新生产要素&#xff0c;算力成新基础能源&#xff0c;大模型成新生产工具&#xff0c;“AI”转型势不可挡。模型参数量突破万亿&#xff0c;对算力需求升级&#xff0c;超万卡…

Docker学习笔记(2)- Docker的安装

1. Docker的基本组成 镜像&#xff08;image&#xff09;&#xff1a;Docker镜像就像是一个模板&#xff0c;可以通过这个模板来创建容器服务。通过一个镜像可以创建多个容器。最终服务运行或者项目运行就是在容器中。容器&#xff08;container&#xff09;&#xff1a;Docker…

Ansible概述

目录 一、ansible简介 二、absible的特点 三、ansible的工作原理以及流程 四、ansible环境安装部署 五、ansible命令行模块 六、inventory 主机清单 一、ansible简介 Ansible是一个基于Python开发的配置管理和应用部署工具&#xff0c;现在也在自动化管理领域大放异彩。…

MT1341-MT1350 码题集 (c 语言详解)

MT1341反比例函数 c 语言实现代码 #include <stdio.h>double f(double x) { return 1.0 / x; }double trapezoidal_integration(double a, double b, int n) {// computer step lengthdouble h (b - a) / n;// computer points valuedouble sum (f(a) f(b)) / 2.0;//…