数据结构之堆排序以及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&…

C#网络编程(System.Net.Sockets命名空间)

目录 一、Socket类 1.示例源码 2.生成效果 二、TcpClient类和TcpListener类 1.示例源码 2.生成效果 三、UdpClient类 1.示例源码 2.生成效果 System.Net.Sockets命名空间主要提供制作Sockets网络应用程序的相关类&#xff0c;其中Socket类、TcpClient类、TcpListener类…

NAND Flash和NOR Flash的异同

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

VSCode修改C++版本

新下载了一下VSCode&#xff0c;想使用C17的特性std::optional&#xff0c;但是显示有错误&#xff0c;想想可能是C 版本的问题&#xff0c;查了一下资料&#xff0c;按下面的博客操作&#xff0c;果然解决了。 vscode设置c 版本

Android跨进程通信,binder,native层,服务端在servicemanager注册服务

文章目录 Android跨进程通信&#xff0c;binder&#xff0c;native层&#xff0c;服务端在servicemanager注册服务1.服务端注册服务请求指令2.svcmgr_publish注册服务3.服务注册完毕通过服务端 Android跨进程通信&#xff0c;binder&#xff0c;native层&#xff0c;服务端在se…

展开说说:Android之广播接收者

1、是什么&#xff1a; BroadcastReceiver是Android四大组件之一的消息型组件&#xff1b;只有一个生命周期onReceiver但不可以进行耗时操作。可以代码动态注册也可清单文件静态注册&#xff1b; 2、啥原理&#xff1a; 广播分为广播发送者和广播接收者&#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;分为集成网络变压器和非集成网络变…

使用进程池/线程池 加速 Python数据处理

使用进程池/线程池 加速 Python数据处理 目标简单模式多进程模式 参考 Python 是一种出色的编程语言&#xff0c;可用于处理数据和自动执行重复任务。尽管 Python 使编码变得有趣&#xff0c;但它并不总是运行速度最快的。默认情况下&#xff0c;Python 程序使用单个 CPU 作为单…

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;绞尽脑汁去找图片了。 …

原型设计模式

4. 原型设计模式 4.1 浅拷贝 在Java编程中&#xff0c;浅拷贝是指在复制对象时&#xff0c;只复制对象的基本数据类型的值和引用类型的地址&#xff0c;不复制引用类型指向的对象本身。浅拷贝可以用于一些简单的场景&#xff0c;例如对象的基本属性不包含其他对象的引用类型&…

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

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

C语言速通笔记(1-40)

1&#xff0e;一个 C 语言程序有且只有一个 main 函数&#xff0c;是程序运行的起点。 2&#xff0e;每个 C 语言程序写完后&#xff0c;都是先编译&#xff0e; c 后链接&#xff0e; obj 最后运行&#xff0e;. exe 3.c和&#xff0e; obj 文件是无法运行的&#xff0c;只有…

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

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

Redis基础知识

目录 一、为什么要用到Redis? 二、Redis 为什么运行比较快&#xff1f; 三、Redis的数据结构 四、Redis可以实现什么功能&#xff1f; 五、Redis 保证数据持久化方式 1、持久化方式主要有2种&#xff1a;RDB 和 AOF 2、RDB和AOF区别&#xff1f; 六、Redis 中的过期删…