数据结构之堆排序以及Top-k问题详细解析

个人主页:点我进入主页

专栏分类:C语言初阶      C语言程序设计————KTV       C语言小游戏     C语言进阶

C语言刷题       数据结构初阶

欢迎大家点赞,评论,收藏。

一起努力

目录

1.前言

2.堆排序

2.1降序排序

2.2时间复杂度

3.Top-k问题

4.总结


1.前言

        在上一篇文章中我们主要讲解了关于大堆和小堆的代码实现,今天我们主要讲解关于堆排序以及堆排序的时间复杂度,我们会讲解关于经典的Top-k问题进行讲解(其中我会伪造一些数据来展示),今天的内容比上次的内容更加的爽,更有挑战性,其中的奥妙真的无法用语言来形容,接下来就让我们感受一下吧。

2.堆排序

        我们对数组进行降序排序,我们使用堆排序,在这里由于升序和降序的思想基本一致,只需要修改一些符号即可完成转化,所以我们只讲关于降序的内容。

2.1降序排序

        在上次的内容中我们使用向上调整来创建堆,我们是创建小堆还是大堆呢?我们想让数据进行降序,如果我们使用大堆的话堆的第一个数是最大的,我们取出来之后堆的顺序就乱了,我们需要重新进行大堆排序,那么我们的时间复杂度为O(n^2*logn),这比我们的冒泡排序还要慢,所以大堆是不可以的,所以我们选择小堆排序,我们这次依旧使用想上调整,详细代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
void Swap(int* num1, int* num2)
{int temp = *num1;*num1 = *num2;*num2 = temp;
}
void print(int* arr, int size)
{for (int i = 0; i < size; i++)printf("%d ", arr[i]);
}
void AdJustUp(int* arr, int sz,int size)
{assert(arr);int child = sz, parent = (child - 1) / 2;while (child>0){if (arr[child] < arr[parent]){Swap(&arr[child], &arr[parent]);child = parent;parent = (child - 1) / 2;}elsebreak;}
}
void AdJustDown(int* arr, int i, int size)
{assert(arr);int parent = i, child = 2 * parent + 1;while (child<size){if (child + 1 < size && arr[child] >arr[child + 1]){child++;}if (arr[parent] > arr[child]){Swap(&arr[parent], &arr[child]);parent = child;child = 2 * parent + 1;}elsebreak;}
}
void HeapSort(int* arr, int n)
{assert(arr);for (int i = 0; i < n; i++){AdJustUp(arr, i, n);}for (int i = 0; i < n-1; i++){Swap(&arr[0], &arr[n - 1 - i]);AdJustDown(arr, 0, n - 1 - i);}}
int main()
{int arr[10];int n = 10;for (int i = 0; i < n; i++){arr[i] = i;}HeapSort(arr,n);print(arr, n);return 0;
}

我们的运行结构如下:

事实上我们这不是我们的堆排序,真正的堆排序在第一次创建小堆时代码为:

	for (int i = (n - 1 - 1) / 2; i >= 0; i--){AdJustDown(arr, 0, n - 1 - i);}

向下调整为什么可以实现呢?,我们知道向下调整是左边和右边都是小堆然后根节点是新插入的我们就可以利用向下调整进行排序,那我们在最后一个节点的父节点进行向下调整,让他们都成为小堆,这样我们就可以完成小堆的创建。那为什么采用这种形式呢?仅仅是因为代码少吗?事实上这与我们的时间复杂度有关。

2.2时间复杂度

        我们看利用向上调整建立小堆的时间复杂度,我们第k层有2^(k-1)个节点,每个节点需要向上调整k-1次共调整(k-1)*2^(k-1)次,第k-1层有2^(k-2),每个节点需要调整k-2次,共调整(k-2)*2^(k-2)……第二层有2^1个节点,每个节点需要调整1次,第一层有2^0个节需要调整0次,共需要调整T(k)=0*2^0+1*2^1+……+(k-2)*2^(k-2)+(k-1)*2^(k-1),我们化简可以得到T(k)=(k-2)2^k+2;其中k=logN,所以T(k)=NlogN;但是我们采用向下调整我们第k层有我们第k层有2^(k-1)个节点,每个节点需要向上调整0次共调整0*2^(k-1)次,第k-1层有2^(k-2),每个节点需要调整1次,共调整1*2^(k-2)……第二层有2^1个节点,每个节点需要调整k-2次,第一层有2^0个节需要调整k-1次,共需要调整T(k)=(k-1)*2^0+(k-2)*2^1+……+1*2^(k-2)+0*2^(k-1),我们化简得到T(k)=2^k-k-1,其中k=logN,故T(k)=N-logN;可以看到向下调整建立堆时间复杂度低,所以我们选择向下调整这大大减少了我们的运算时间。

3.Top-k问题

        有一个问题是我们在一组数中(共N个数)找到最小的k个数,其中N远大于k,让我们找到前k个数,当数据很小的时候我们利用堆排序进行查找很容易,但是当数据量特别大的时候我们就很难实现,因为数据占用的内存太大了,例如我们要在1百亿个数据中找到前10个最小的数,100万个整形数据相当于占用37GB,这样我们就很难处理,这时候就出现了我们的Top-k问题,我们是如何解决这个问题呢?这时候我们由于需要找最小的前10个数据我们创建一个大堆,然后输入一个数据就将堆顶元素替换然后再向下调整这样就可以找到最小的10个数据,我们创建100万个数据进行模拟,我们的代码如下:

我们将数据放在文件中,生成data.txt文件

#include<stdio.h>
int main()
{FILE* pf = fopen("data.txt", "w");if (pf == NULL){perror("fopen fail");return 1;}for (int i = 0; i < 1000000; i++){fprintf(pf,"%d\n", i);}fclose(pf);pf = NULL;return 0;
}

修改其中的10个数据让他成为我们的结果,然后进行下一步找到这k个数

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int MyHeapData;
typedef struct Heap {MyHeapData* data;int size;int capacity;
}Heap;
void HeapInit(Heap* php)
{assert(php);php->data = (MyHeapData*)malloc(sizeof(MyHeapData)*10);php->size = 0;
}
void Swap(int* num1, int* num2)
{int temp = *num1;*num1 = *num2;*num2 = temp;
}
void AdJustDown(int* arr, int n, int i)
{assert(arr);int parent = 0, child = 2 * parent + 1;while (child<n){if (child+1<n&&arr[child] < arr[child + 1]){child++;}if (arr[parent] < arr[child]){Swap(&arr[parent], &arr[child]);parent = child;child = parent * 2 + 1;}elsebreak;}
}
void AdJustUp(MyHeapData* arr, int size)
{assert(arr);int child = size - 1, parent = (child - 1) / 2;while (child > 0){if (arr[child] > arr[parent]){Swap(&arr[child], &arr[parent]);child = parent;parent = (child - 1) / 2;}elsebreak;}
}
int main()
{//FILE* pf = fopen("data.txt", "w");//if (pf == NULL)//{//	perror("fopen fail");//	return 1;//}//for (int i = 0; i < 1000000; i++)//{//	fprintf(pf,"%d\n", i);//}//fclose(pf);//pf = NULL;FILE* pf = fopen("data.txt", "r");if (pf == NULL){perror("fopen fail");return 1;}int data;int i;Heap ph ;HeapInit(&ph);for (i = 0; i < 10; i++){fscanf(pf, "%d", &data);ph.data[i] = data;AdJustUp(ph.data, i);}while (fscanf(pf, "%d", &data) != EOF){if(data<ph.data[0])Swap(&data, &ph.data[0]);AdJustDown(ph.data, 10, 0);}for (i = 0; i < 10; i++){printf("%d ", ph.data[i]);}fclose(pf);pf = NULL;return 0;
}

运行结果如下:

这就是我们经典的Top-k问题;

4.总结

        今天的内容到这里就结束了,希望大家可以好好的理解今天的内容,欢迎大家来三连。

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

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

相关文章

Prime 1.0

信息收集 存活主机探测 arp-scan -l 或者利用nmap nmap -sT --min-rate 10000 192.168.217.133 -oA ./hosts 可以看到存活主机IP地址为&#xff1a;192.168.217.134 端口探测 nmap -sT -p- 192.168.217.134 -oA ./ports UDP端口探测 详细服务等信息探测 开放端口22&#x…

【Vulnhub 靶场】【HackathonCTF: 2】【简单】【20210620】

1、环境介绍 靶场介绍&#xff1a;https://www.vulnhub.com/entry/hackathonctf-2,714/ 靶场下载&#xff1a;https://download.vulnhub.com/hackathonctf/Hackathon2.zip 靶场难度&#xff1a;简单 发布日期&#xff1a;2021年06月20日 文件大小&#xff1a;2.6 GB 靶场作者&…

54.多级缓存

目录 一、传统缓存的问题、多级缓存方案。 二、JVM进程缓存。 1&#xff09;进程缓存和缓存。 2&#xff09;导入商品案例。 1.安装MySQL 2.导入SQL 3.导入Demo工程 4.导入商品查询页面 3&#xff09;初识Caffeine&#xff08;就是在springboot学过的注解方式的cache&…

NAND Flash和NOR Flash的异同

NAND Flash和NOR Flash是两种常见的闪存类型。 NOR Flash是Intel于1988年首先开发出来的存储技术&#xff0c;改变了原先由EPROM和EEPROM一统天下的局面。 NAND Flash是东芝公司于1989年发布的存储结构&#xff0c;强调降低每比特的成本&#xff0c;更高的性能&#xff0c;并…

栈和队列OJ题——15.循环队列

15.循环队列 622. 设计循环队列 - 力扣&#xff08;LeetCode&#xff09; * 解题思路&#xff1a; 通过一个定长数组实现循环队列 入队&#xff1a;首先要判断队列是否已满&#xff0c;再进行入队的操作&#xff0c;入队操作需要考虑索引循环的问题&#xff0c;当索引越界&…

网络接口规范

1、基本物理层: a) RJ45接口作为最基本的网络接口之一有两种形式&#xff1a;对于百兆网口有4条线&#xff0c;2对差分线&#xff1b;对于千兆网口有4对差分线。RJ45水晶头是有8个凹槽和8个触点&#xff08;8p8c&#xff09;的接头&#xff0c;分为集成网络变压器和非集成网络变…

2022年9月8日 Go生态洞察:Go Developer Survey 2022 Q2 结果分析

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

物奇平台电容触摸功能调试

是否需要申请加入数字音频系统研究开发交流答疑群(课题组)?可加我微信hezkz17, 本群提供音频技术答疑服务,+群赠送语音信号处理降噪算法,蓝牙耳机音频,DSP音频项目核心开发资料, 物奇平台电容触摸功能调试 1 修改按键驱动宏 2 编译生成wpk 文件,import 导入烧录文件。…

水果编曲软件fl studio手机版下载

fl studio mobile手机版中文名水果编曲软件&#xff0c;它是一款非常不错的音乐编曲软件&#xff0c;凭借简单易上手的操作方式&#xff0c;强悍且实用的功能&#xff0c;深受到了音乐创作者的喜爱&#xff0c;不仅仅提供了广阔的音乐创作空间&#xff0c;可以让用户对舞曲、轻…

工具网站:随机生成图片的网站

一个随机生成图片的网站&#xff1a;Lorem Picsum。 有时候&#xff0c;我们做静态页面需要大量图片去填充内容&#xff0c;以使用该网站去生成指定尺寸的图片。每次打开页面都会获取不同的图片&#xff0c;就不用我们做静态页面开发的时候&#xff0c;绞尽脑汁去找图片了。 …

振南技术干货集:ChatGPT,现在我做单片机/嵌入式开发已经离不开它了!(2)

注解目录 &#xff08;此文部分内客由 ChatGPT 生成&#xff0c;你分得出来哪些是人写的&#xff0c;哪些是 ChatGPT 生成的吗?&#xff09; 20.1 恐怖的 ChatGPT 2023年ChatGPT有多火?比 TikTok火4 倍都不止!什么是“范式革命”?从石器时代到飞机大炮就是范式革命。AI绘…

Python读取栅格遥感影像并加以辐射校正后导出为Excel的一列数据

本文介绍基于Python语言中的gdal模块&#xff0c;读取一景.tif格式的栅格遥感影像文件&#xff0c;提取其中每一个像元的像素数值&#xff0c;对像素值加以计算&#xff08;辐射定标&#xff09;后&#xff0c;再以一列数据的形式将计算后的各像元像素数据保存在一个.csv格式文…

IDA常用操作、快捷键总结以及使用技巧

先贴一张官方的图&#xff0c;然后我再总结一下&#xff0c;用的频率比较高的会做一些简单标注 快捷键 F系列【主要是调试状态的处理】 F2 添加/删除断点F4 运行到光标所在位置F5 反汇编F7 单步步入F8 单步跳过F9 持续运行直到输入/断点/结束 shift系列【主要是调出对应的页…

【RotorS仿真系列】Ardrone模型介绍

ardrone是rotors仿真框架提供的一款机型&#xff0c;因为该机型与我们实际使用的机型参数相近&#xff0c;所以这里对它的参数做特别整理和记录。 一、模型参数总结 ardrone的gazebo模型如下图所示&#xff1a; 根据ardrone.yaml&#xff0c;其关键参数如下所示&#xff1a…

Project 1: The Game of Hog(CS61A)

&#xff08;第一阶段&#xff09;问题 5a&#xff08;3 分&#xff09; 实现该函数&#xff0c;该函数模拟了完整的 Hog 游戏。球员 交替轮流掷骰子&#xff0c;直到其中一名玩家达到分数。playgoal 您现在可以忽略 Feral Hogs 规则和论点; 您将在问题 5b 中实现它。feral_h…

树莓派多串口通信

树莓派多串口通信 串口配置串口通信函数分析串口通信示例代码 参考博文1&#xff1a;树莓派 4 UART 多串口配置通信参考博文2&#xff1a;树莓派wiringPi库详解关于树莓派相关其他环境配置可参考&#xff1a;快速上手树莓派关于wiringPi库初始化与IO口开发可参考&#xff1a;树…

调优--学习笔记

1&#xff0c;Presto调优 数据存储格式 1&#xff09;合理设置分区 与Hive类似&#xff0c;Presto会根据元信息读取分区数据&#xff0c;合理的分区能减少Presto数据读取量&#xff0c;提升查询性能。 2&#xff09;使用列式存储 Presto对ORC文件读取做了特定优化&#xff0c…

Qt OpenCV 学习(一):环境搭建

对应版本 Qt 5.15.2OpenCV 3.4.9MinGW 8.1.0 32-bit 1. OpenCV 下载 确保安装 Qt 时勾选了 MinGW 编译器 本文使用 MinGW 编译好的 OpenCV 库&#xff0c;无需自行编译 确保下载的 MinGW 和上述安装 Qt 时勾选的 MinGW 编译器位数一致&#xff0c;此处均为 x86/32-bit下载地址…

《微信小程序开发从入门到实战》学习四十

4.2 云开发JSON数据库 4.2.11 更新数据 使用数据库API更新数据有两种方法&#xff1a;一.将记录局部更新的update方法&#xff1b;二.以替换的方式更新记录的set方法 update方法可以局部更新一个记录或一个集合的多个记录&#xff0c;更新时只有指定字段更新&#xff0c;其他…

智能诊疗体验:整合AI技术的互联网医院小程序开发

在科技化的趋势下&#xff0c;互联网医院小程序的开发变得愈发重要&#xff0c;尤其是通过整合人工智能&#xff08;AI&#xff09;技术&#xff0c;进一步提升了就医的效率。 一、引言 互联网医院小程序其开发目标是提高医疗服务的效率&#xff0c;同时也也提升了用户的就医…