【数据结构初阶第十五节】堆的应用(堆排序 + Top-K问题)

必须有为成功付出代价的决心,然后想办法付出这个代价。云边有个稻草人-CSDN博客

对于本节我们要提前掌握前一节课堆的相关实现才能学好本次的知识,一定要多画图多敲代码看看实现的效果是啥(Crazy!)开始吧!

目录

一、堆排序

(一) 基于原有堆

(二) 原数组上直接建堆

1.向上调整算法建堆

2.向上调整算法建堆时间复杂度

3.向下调整算法建堆

4.向下调整算法建堆时间复杂度

二、TOP-K问题

        ——————————————《Being in love》——————————————  


一、堆排序

(一) 基于原有堆

结合下面的代码观看——创建一个数组,将数组里面的数据不断地入堆后建立了一个堆(假设是一个小堆),不断取堆顶数据打印后出堆(此操作循环),这样就可以实现排序。为什么这样就实现了排序呢?Because小堆的堆顶是堆里面的最小值,出堆时向下调整又变成了小堆,此时堆顶是剩下元素里面的最小值,就这样不断取堆顶(最小值)实现了升序操作。

但是,这样的排序方法我们必须提前实现一个堆,而且我们实现堆操作时至少要申请一块原排序数组大小的空间,空间复杂度为O(N),那该如何调整排序算法使空间复杂度变为O(1)呢?接下来看第二个排序方法

//1.需要堆的数据结构
//2,空间复杂度为O(N)
void HeapSort(int* arr.int n)
{
    HP hp;
    for (int i = 0; i < n; i++)
    {
    	HPPush(&hp, arr[i]);
    }

    int i = 0;
    while(!HPEmpty(&hp))
    {
	    arr[i++] = HPTop(&hp));
	    HPPop(&hp);
    }
    HPDestroy(&hp);
}

(二) 原数组上直接建堆

1.向上调整算法建堆

原数组里建堆,首尾交换,交换后的堆尾数据从堆中删掉,将堆顶数据向下调整选出次大的数据  。

void HeapSort(int* arr, int n)
{
	//小堆->降序
    //大堆->升序
	for (int i = 0; i < 6; i++)
	{
		AdjustUp(arr, i);
	}

	//堆排序
	int end = n - 1;
	while (end > 0)
	{
		Swap(&arr[0], &arr[end]);//end指向的是最后一个数据
		AdjustDown(arr, 0, end);//有效的数据个数减了1
		end--;
	}
}

上面是实现了排降序的操作,如何实现排升序的操作呢?异曲同工之妙!来看

上面建的是小堆,现在我们要在原数组内建大堆才能实现排升序的操作,如何建大堆呢?见下面的代码(要动手画图看看效果是如何实现建大堆的)

//向上调整数据,实现建堆操作
void AdjustUp(HPDatatype* arr, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
        //建小堆,谁小谁往上放,用 <
        //建大堆,谁大谁往上放,用 >
		if (arr[child] > arr[parent])
		{ 
			Swap(&arr[child], &arr[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
//向下调整数据
void AdjustDown(HPDatatype* arr, int parent, int n)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		//找出左右孩子中最小的->小堆 >
		//找出左右孩子中最大的->大堆 <
		if (child + 1 < n && arr[child] < arr[child + 1])
		{
			child++;
		}
        // < 建小堆
        // > 建大堆 
		if (arr[child] < arr[parent])
		{
			Swap(&arr[parent], &arr[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}	
}

2.向上调整算法建堆时间复杂度

上面第二种方法我们实现的堆排序把空间复杂度降为O(1),直接在原数组里面建堆不需要额外申请空间,那时间复杂度如何呢?

void HeapSort(int* arr, int n)
{
	//小堆->降序
    //大堆->升序
	for (int i = 0; i < 6; i++)
	{
		AdjustUp(arr, i);
	}

	//堆排序
	int end = n - 1;
	while (end > 0)
	{
		Swap(&arr[0], &arr[end]);//end指向的是最后一个数据
		AdjustDown(arr, 0, end);//有效的数据个数减了1
		end--;
	}
}

 【大致估算】

结合排序代码思考,排序操作里面先是向上调整算法,假设堆的度为k,那么向上调整算法最差要向上交换k-1次;我们根据节点数来计算交换的次数,我们知道 2^k -1 = n(n为总结点的个数),k = log(n+1) -> k = log(n),这只是插入一个结点,若要插入m个结点,就是m*log(n)次,因为向下调整算法也是这样,所以加起来就是O(2*m*log(n)),也就是O(m*log(n)),这只是大致计算一下时间复杂度。

【细致计算】 

3.向下调整算法建堆

不仅仅可以向上调整算法建堆,还可以向下调整算法建堆。(根据代码原理自己动手画画图)

void HeapSort(int* arr, int n)
{
	//向下调整算法建堆
	for (int i = (n - 2) / 2; i >= 0; i--)
	{
		AdjustDown(arr, i, n);
	}

	//堆排序
	int end = n - 1;
	while (end > 0)
	{
		//end指向的是最后一个数据
		Swap(&arr[0], &arr[end]);
		//有效的数据个数减了1
		AdjustDown(arr, 0, end);
		end--;
	}
}
4.向下调整算法建堆时间复杂度

感觉有点乱理一理先

实现堆排序 = 建堆 + 排序

如何建堆? 两种方法 --> 向上调整算法建堆 / 向下调整算法建堆

建大堆还是建小堆取决于你想排升序还是排降序,自行选择

建大堆 --> 升序
建小堆 --> 降序

二、TOP-K问题

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

(1)用数据集合中前K个元素来建堆(自己好好想清楚)

  • 取前K个最大的元素,则建小堆
  • 取前K个最小的元素,则建大堆 

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

  •  对于建小堆,若剩余元素比堆顶元素大则交换;
  •  对于建大堆,若剩余元素比对顶元素小则交换。

时间复杂度为:我们采用向下调整算法建堆,时间复杂度为O(K),之后将N-K个数据进行向下调整,时间复杂度为(N-K)log(K) ,加在一起将小影响的忽略就是O(N) 。 注意看自己AdjustDown实现的是大堆还是小堆。

void CreateNDate()
{
	// 造数据
	int n = 100000;
	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) % 1000000;
		fprintf(fin, "%d\n", x);
	}

	fclose(fin);
}

void Top()
{
	printf("请输入k:");
	int k = 0;
	scanf("%d", &k);
	const char* file = "data.txt";
	FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		perror("fopen error!");
		return;
	}
    
    //申请一块堆大小的空间
	int* minHeap = (int*)malloc(k * sizeof(int));
	if (minHeap == NULL)
	{
		perror("malloc file!");
		exit(2);
	}

	//将数据存入堆里面
	for (int i = 0; i < k; i++)
	{
		fscanf(fout, "%d", &minHeap[i]);
	}

	//建堆,通过向下调整算法建一个小堆
	for (int i = (k - 2) / 2; i >= 0; i--)
	{
		AdjustDown(minHeap, i, k);
	}

	//将剩下的数据进行过比较,满足的与堆顶数据进行交换
	int x = 0;
	while (fscanf(fout, "%d", &x) != EOF)
	{
		if (x > minHeap[0])
		{
			minHeap[0] = x;
			AdjustDown(minHeap, 0, k);
		}
	}

	for (int i = 0; i < k; i++)
	{
		printf("%d ", minHeap[i]);
	}

	fclose(fout);

}

int main()
{
	CreateNDate();
	Top();
	return 0;
}

终于结束了! (我要疯了真的)               

完——


   ——————————————《Being in love》——————————————  

 Being in Love_Synth Monsters、川岛三离_高音质在线试听_Being in Love歌词|歌曲下载_酷狗音乐

 [正是你对你的玫瑰花费的时光,才使你的玫瑰如此重要]                                                                          

至此结束!

我是云边有个稻草人

期待与你的下一次相遇。。。 

                                                  

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

deepseek自动化代码生成

使用流程 效果第一步&#xff1a;注册生成各种大模型的API第二步&#xff1a;注册成功后生成API第三步&#xff1a;下载vscode在vscode中下载agent&#xff0c;这里推荐使用cline 第四步&#xff1a;安装完成后&#xff0c;设置模型信息第一步选择API provider&#xff1a; Ope…

Scrapy:Downloader下载器设计详解

Scrapy下载器设计详解 1. 整体架构 Scrapy的下载器(Downloader)是整个爬虫框架的核心组件之一&#xff0c;负责处理所有网络请求的下载工作。它的主要职责是&#xff1a; 管理并发请求实现请求调度处理下载延迟维护下载槽(Slot) 官方文档&#xff1a;Settings中的Downloader配…

【IO】java IO流的类型及IO模型

文章目录 分类字节流输入流输出流 字符流输入流输出流 字节缓冲流字符缓冲流4中常见的IO模型BIO&#xff08;同步阻塞模型&#xff09;同步非阻塞模型NIO&#xff08;多路复用模型&#xff09;AIO异步 分类 根据数据流向分为&#xff1a;输入流、输出流&#xff08;以内存为中…

计算机视觉:主流数据集整理

第一章&#xff1a;计算机视觉中图像的基础认知 第二章&#xff1a;计算机视觉&#xff1a;卷积神经网络(CNN)基本概念(一) 第三章&#xff1a;计算机视觉&#xff1a;卷积神经网络(CNN)基本概念(二) 第四章&#xff1a;搭建一个经典的LeNet5神经网络(附代码) 第五章&#xff1…

八股文实战之JUC:静态方法的锁和普通方法的锁

1、对于staic同步方法锁住的是class类模板&#xff08;Class对象&#xff09; 对象是线程&#xff08;调用者&#xff09; 调用者只有获取资源的锁才能调用 2、普通同步方法 锁住的资源是class对象 对象是线程&#xff08;调用者&#xff09;即&#xff1a; 静态同步方法&a…

EasyRTC:基于WebRTC与P2P技术,开启智能硬件音视频交互的全新时代

在数字化浪潮的席卷下&#xff0c;智能硬件已成为我们日常生活的重要组成部分&#xff0c;从智能家居到智能穿戴&#xff0c;从工业物联网到远程协作&#xff0c;设备间的互联互通已成为不可或缺的趋势。然而&#xff0c;高效、低延迟且稳定的音视频交互一直是智能硬件领域亟待…

VSCode - VSCode 切换自动换行

VSCode 自动换行 1、基本介绍 在 VSCode 中&#xff0c;启用自动换行可以让长行代码自动折行显示&#xff0c;避免水平滚动条频繁使用&#xff0c;提升代码阅读体验 如果禁用自动换行&#xff0c;长行代码就需要手动结合水平滚动条来阅读 2、演示 启用自动换行 禁用自动换…

编程小白冲Kaggle每日打卡(12)--kaggle学堂:<机器学习简介>模型如何工作

Kaggle官方课程链接&#xff1a;How Models Work 本专栏旨在Kaggle官方课程的汉化&#xff0c;让大家更方便地看懂。 How Models Work 第一步&#xff0c;如果你是机器学习的新手。 Introduction 我们将从概述机器学习模型的工作原理和使用方法开始。如果你以前做过统计建模…

IDEA安装deepseek最新教程2025

IDEA引入DeepSeek 将 IntelliJ IDEA&#xff08;JetBrains 开发的 Java 集成开发环境&#xff09;与 DeepSeek&#xff08;深度求索的技术能力&#xff09;结合&#xff0c;通常涉及利用 AI 技术增强开发效率或扩展 IDE 功能,安装完成后&#xff0c;结合 IntelliJ IDEA 的开发…

安科瑞能源物联网平台助力企业实现绿色低碳转型

安科瑞顾强 随着全球能源结构的转型和“双碳”目标的推进&#xff0c;能源管理正朝着智能化、数字化的方向快速发展。安科瑞电气股份有限公司推出的微电网智慧能源管理平台&#xff08;EMS 3.0&#xff09;&#xff0c;正是这一趋势下的创新解决方案。该平台集成了物联网&…

Ansible 学习笔记

这里写自定义目录标题 基本架构文件结构安装查看版本 Ansible 配置相关文件主机清单写法 基本架构 Ansible 是基于Python实现的&#xff0c;默认使用22端口&#xff0c; 文件结构 安装 查看用什么语言写的用一下命令 查看版本 Ansible 配置相关文件 主机清单写法

android,flutter 混合开发,pigeon通信,传参

文章目录 app效果native和flutter通信的基础知识1. 编解码器 一致性和完整性&#xff0c;安全性&#xff0c;性能优化2. android代码3. dart代码 1. 创建flutter_module2.修改 Android 项目的 settings.gradle&#xff0c;添加 Flutter module3. 在 Android app 的 build.gradl…

怎么在Github上readme文件里面怎么插入图片?

环境&#xff1a; Github 问题描述&#xff1a; 怎么在Github上readme文件里面怎么插入图片&#xff1f; https://github.com/latiaoge/AI-Sphere-Butler/tree/master 解决方案&#xff1a; 1.相对路径引用 上传图片到仓库 将图片文件&#xff08;如 .png/.jpg&#xff…

论文略读:Uncovering Hidden Representations in Language Models

202502 arxiv 说一下主要结论吧 对于下游任务&#xff0c;语言模型的中间层在所有架构和任务中始终优于最后一层 这挑战了使用最后一层表示的传统观点。 不同的架构表现出不同的信息压缩模式。自回归模型在中间层存在瓶颈&#xff0c;而双向模型则保持更均匀的趋势 BERT通过双…

0基础学Linux系统(准备1)

知识拓展 首先了解一下操作系统的作用与常见的操作系统。 我们身边常见的操作系统是windows,MACOS&#xff0c;Linux等&#xff0c;手机的操作系统有iOS&#xff0c;安卓等。 这里要学的就是Linux操作系统。 一个可用的计算机&#xff0c;可以说是由硬件和软件一起组成的&a…

在VS中如何将控制台(console)项目改为窗口(window)项目

1. 修改属性&#xff1a; 2. 修改main函数 int WINAPI WinMain(_In_ HINSTANCE hInstance,_In_opt_ HINSTANCE hPrevInstance,_In_ LPSTR lpCmdLine,_In_ int nShowCmd) //int main()

区块链共识机制详解

区块链共识机制详解 &#x1f91d; 1. 什么是共识机制&#xff1f; 共识机制是区块链网络中&#xff0c;所有节点就某个状态&#xff08;如交易的有效性&#xff09;达成一致的规则和过程。它解决了在去中心化网络中如何确保数据一致性的问题。 2. 主流共识机制 2.1 工作量证…

【项目设计】自主HTTP服务器

目录 项目介绍 网络协议栈介绍 协议分层 数据的封装与分用 HTTP相关知识介绍 HTTP的特点 URL格式 URI、URL、URN HTTP的协议格式 HTTP响应协议格式 HTTP的请求方法 HTTP的状态码 HTTP常见的Header CGI机制介绍 CGI机制的概念 CGI机制的实现步骤 CGI机制的意义 …

阿里云k8s服务部署操作一指禅

文章目录 DockerFile镜像操作阿里云k8s服务部署 DockerFile # 使用 JDK 17 官方镜像 # linux架构&#xff1a;FROM --platformlinux/amd64 openjdk:17-jdk-slim # arm架构&#xff1a;openjdk:17-jdk-slim FROM --platformlinux/amd64 openjdk:17-jdk-slim# 设置工作目录 WORK…

lattice hdl实现spi接口

在lattice工具链中实现SPI接口通常涉及以下步骤: 定义硬件SPI接口的管脚。配置SPI时钟和模式。编写SPI主机或从机的控制逻辑。 展示了如何在Lattice工具链中使用HDL语言(例如Verilog)来配置SPI接口: lattice工程 顶层:spi_slave_top.v `timescale 1ns/ 1ps module spi_…