数据结构——利用堆进行对数组的排序

在这里插入图片描述

今天文章的内容是关于我们如何利用堆的特性对我们的数组进行排序,还有就是我们的TopK的问题,这次我们放在的是文件种,我们放入一亿个数字,然后我们取出一亿个数字中最大的十个数,利用上章堆的问题进行解决。

首先就是我们如果对一个数组要进行排序,这个数组是没有任何规律的,就像下面的这个数组。

int arr[] = { 9,4,3,19,12,13,5,8,9 };

那我们得利用我们堆的特性,因为我们知道堆的特性,首先堆顶的数据一定是最小的,那我们要进行排序之前的话,要做的一个最重要的步骤就是先建立一个堆出来,我们可以用两种方法,一种是向上建堆,另一种就是向下建堆,这两个方法我们都会讲。

向上建堆

首先我们这里给的例子是升序,但是在升序的时候,我们是建立大堆还是小堆呢?答案是大堆,那我们先来看看减小堆的时候,会产生怎样的问题,再来看看大堆,两者相互比较之后,我们就会发现升序就应该建立大堆。

首先就是复用我们上次堆的内容的向上建堆的那个方法,就是AdjustUp,如果这里大家不明白可以回头看看,我这里直接给出代码。

void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}
void AdjustUp(int* a, int child)
{int parent = (child - 1) / 2;while (child > 0){if (a[child] < a[parent]){Swap(&a[child], &a[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}

我们可以看到这是向上调整,那我们建堆的过程是不是从二叉树的第二层开始往上建堆,比较的就是孩子和父亲的关系,那我们这里就可以写一个循环来完成这个建立堆的过程。

int arr[] = { 9,4,3,19,12,13,5,8,9 };for (int i = 1; i < sizeof(arr) / sizeof(arr[0]); i++){AdjustUp(arr, i);}

然后我们这个过程建立出来堆的样子就是我们的小堆

在这里插入图片描述
小堆建好之后,我们后面一步就是进行排序,但是排序有个问题,虽然我们保证了一开始堆顶的元素是最下的,但是我们怎么找出第二小,和第三小的数,如果我们这里有人说,我们可以利用堆的向下调整方法,然后重新建立堆,找出次小的,一次这样往下走就没问题,虽然说这样是可以完成排序,但是这样的排序方法甚至比冒泡还慢,看似用了堆的特性,其实时间复杂度比冒泡排序还高,那这样就没能完成我们堆的作用,但是如果我们建立大堆的话,结果·就·有所·不一样,我们可以不找小的,我们先排序后面的。

在这里插入图片描述
建立大堆后的样子,那我们可以先交换第一个元素和最后一个元素,然后再进行 向下调整,我们后面也会详细计算向下调整和向上调整的时间复杂度的,我们先来看如果交换第一个和最后一个元素的位置,那就变成下面这样。
在这里插入图片描述
这是进行交换之后的样子,但是还是有问题,我们要保证我们这个还是大堆,那该怎么做呢,首先就是得向下调整,向下调整就是堆顶的元素往下调整,我们利用堆的特性之间写的AdjustDown,调整好之后,是下面的这个图。

在这里插入图片描述
这个时候我们发现最后一个元素是最大的,有序的,而且我们还是大堆,那现在堆顶的元素就是次·大的数,所以现在要做的就是第一个和倒数第二个换位置,然后再进行调整,这样倒数两个的就是有序,有序之后他还是大堆,堆顶的元素就是第三个最大的数,这样一次循环,一直到最后就变成有序了。

那我们的代码就是下面这个,其实代码很简答的主要可能难理解。

AdjustDown
void AdjustDown(int* a, int size, int parent)
{int child = 2 * parent + 1;while (child < size){if (a[child + 1] > a[child] && child+1 < size){child++;}if (a[child] > a[parent]){Swap(&a[child], &a[parent]);parent = child;child = 2 * parent + 1;}else{break;}}
}

这个就是AdjustDown的代码,再堆里讲过,这样就不将了,来看我们如何进行排序的部分代码

int end = n - 1;while (end > 0){Swap(&arr[0], &arr[end]);AdjustDown(arr, end, 0);//这里的end是元素个数,如果是下标的话就是指最后一个元素的后一个end--;}

end = 0的时候就说明已经排序好了,所以这个就是判断条件,然后来看我们的end一开始就是指向最后一个元素,因为是数组,所以这里表示的就是下标,我们这里就是要注意这个,然后先是交换堆顶元素和最后一个元素的问题,就直接开始调整,但是调整的时候我们end并没有进行–,因为AdjustDown的size位置的参数表示的就是元素个素,然后我们调整的时候因为最后一个元素已经有序了,所以就不用在进行调整了。

我们来看看结果
在这里插入图片描述
可以发现我们也是排好序了,这里呢还有要讲一个内容就是建堆,我们那个时候建堆是向上调整,是从第二层开始的,我们也可以用向下建堆的方法,向下建堆要保证两边的子树都是堆,比如我们现在是大堆,所以子数就得是大堆,我们第一次进行调整得应该是第一个父亲节点,我们可以用(size - 1- 1)/ 2找到第一个父亲节点。因为我们堆虽然看起来是个二叉树,但是实际上就是一个数组,我们这里来看代码是如何实现得。

int main()
{int arr[] = { 9,4,3,19,12,13,5,8,9 };int n = sizeof(arr) / sizeof(arr[0]);for (int i = (n - 1 - 1) / 2; i >= 0; i--){AdjustDown(arr, n, i);}for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){printf("%d ", arr[i]);}printf("\n");int end = n - 1;while (end > 0){Swap(&arr[0], &arr[end]);AdjustDown(arr, end, 0);//这里的end是元素个数,如果是下标的话就是指最后一个元素的后一个end--;}for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++){printf("%d ", arr[i]);}return 0;
}

这个就是只用了向下调整进行排序,建堆得方法也是用的向下调整得这个方法,那我们后面得来计算一下向上调整和向下调整得时间复杂度,这里先给出得结论就是向下建堆的方法才是最高效的,我们下面给出一个图来分别计算出他们的时间复杂度。
在这里插入图片描述
我们给出这样的一个图,首先就是假设这颗数的高度就是H,然后我们在旁边写出他们每一层的节点数。

在这里插入图片描述
那我们可以再来计算一下他们如果是向上调整的话需要进行的调整次数。
在这里插入图片描述
那这个时候我们只要帮他们相乘起来得到一个需要裂项相消的函数。

在这里插入图片描述
又因为我们的高度和我们节点个数有个等式,我们就可以把h变成N表示,我们来看看。
在这里插入图片描述
这个就是我们向上调整的方式,如果是向下调整的话一样的道理,只是我们是从倒数第二层开始,其实大家自己试试就行了,计算起来是一样的方法,时间复杂度是O(N)我们其实也可以通过分析得出,因为向上调整的方法是和向下的一样的,我这里就讲一个,我们不难看出向上调整的时间复杂度是高于向下的,这是为什么,我们可以看他们最多层,向上调整的时候,我们最多层是最后一层,他的节点数最多,高度也是最高的,所以是多对多,时间复杂度就是要比我们的向下调整,我们向下调整时从最后面的父亲节点开始的,而且只要调整一次就行了,这就是多对少,倒数第二层的节点基本上是整个节点的最后一个,所以我们这里得出的结论就是向下调整是最快的。我们后面就可以直接只用一个向下建堆就可以解决问题了。其实我们这里的排序本质上还是选择排序。
在这里插入图片描述
这个是向下建立堆的计算过程,大家可以看看,实在不会就私信小编,谢谢大家。

还有一个TopK问题放在下一篇文章里,因为这样流量多哈哈哈哈哈,下篇文章见。

在这里插入图片描述

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

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

相关文章

HTML+CSS+JS网页设计

文章目录 作品介绍一、代码演示1.登录、注册&#xff0c;获取当前时间2.轮播图3.家乡简介4.热门景点5.特色美食6.页尾 二、效果图总结 作品介绍 HTML页面主要由&#xff1a;登录、注册跳转页面&#xff0c;轮播图&#xff0c;家乡简介&#xff0c;热门景点&#xff0c;特色美食…

OpenCV入门11——图像的分割与修复

文章目录 图像分割的基本概念实战-分水岭法(一)实战-分水岭法(二)GrabCut基本原理实战-GrabCut主体程序的实现实战-GrabCut鼠标事件的处理实战-调用GrabCut实现图像分割meanshift图像分割视频前后景分离其它对视频前后影分离的方法图像修复 图像分割是计算机视觉中的一个重要领…

羊大师:不同时段喝羊奶,效果会有何不同?

羊大师&#xff1a;不同时段喝羊奶&#xff0c;效果会有何不同&#xff1f; 羊奶是一种营养丰富、口感香浓的健康饮品&#xff0c;被广大消费者所喜爱。然而&#xff0c;你是否知道&#xff0c;不同的喝奶时间对身体的影响也是不同的呢&#xff1f;在不同时段喝羊奶&#xff0…

ECShop 2.x/3.x SQL注入/远程代码执行漏洞

漏洞描述 ECShop是一个B2C独立商店系统&#xff0c;供企业和个人快速建立个性化的在线商店。本系统是一个基于PHP语言和MYSQL数据库架构的跨平台开源程序。 在 2017 年及之前的版本中&#xff0c;存在一个 SQL 注入漏洞&#xff0c;该漏洞可能会注入有效负载并最终导致代码执…

【JavaEE初阶】 博客系统项目--前端页面设计实现

文章目录 &#x1f332;主要内容&#x1f38d;预期效果&#x1f6a9;博客列表页效果&#x1f6a9;博客详情页&#x1f6a9;博客登录页&#x1f6a9;博客编辑页 &#x1f340;实现博客列表页&#x1f6a9;实现导航栏&#x1f388;页面主体部分 &#x1f384;实现博客详情页&…

新闻研究导刊杂志社新闻研究导刊杂志新闻研究导刊编辑部2023年第21期目录

新闻研究导刊杂志社新闻研究导刊杂志新闻研究导刊编辑部2023年第21期目录 研究论文 视频新闻的短视频化趋势与受众接受度分析 郭晓楷; 1-4 新媒体时代铸牢中华民族共同体意识的理论逻辑与实践路径研究 衷振华; 5-7 区域形象名片化构建背景下的新媒体传播策略研究 宋…

MySQL死锁,死锁产生的4个必要条件,死锁案例, 如何避免死锁

文章目录 MySQL死锁了怎么办&#xff08;死锁的产生及解决方案&#xff09;&#xff1f;1、 死锁与产生死锁的四个必要条件1.1 什么是死锁1.2 死锁产生的4个必要条件 2、死锁案例2.1 表锁死锁2.2 行锁死锁2.3 共享锁转换为排他锁 3、死锁排查4、 如何避免死锁5、死锁的排查6、 …

在线教育行业内卷突围,持续激活平台用户体验是关键

在线教育并不等同于K12教育。 众所周知&#xff0c;越来越卷的考试制度&#xff0c;已经被家长、学生们的谩骂和吐槽淹没了好几层。各种减负、杜绝课后补课等条例纷纷出台&#xff0c;不断的挤压着K12教育企业的生存空间。 于是乎&#xff0c;大家都认为&#xff0c;在线教育…

语义SLAM论文、代码和数据集汇总

文章目录 2023Large-scale Autonomous Flight with Real-time Semantic SLAM under Dense Forest Canopy 2023 Large-scale Autonomous Flight with Real-time Semantic SLAM under Dense Forest Canopy 摘要&#xff1a;语义地图使用一组语义上有意义的对象来表示环境。这种…

UCP通信

一&#xff0c;概括 二 &#xff0c;常用方法 三&#xff0c;实现步骤&#xff08;一发一收&#xff09; 四&#xff0c;案例&#xff08;一发一收&#xff09; &#xff08;1&#xff09;&#xff1a;客户端 &#xff08;2&#xff09;&#xff1a;服务端 &#xff08;3&…

C语言键盘输入字符串小写转大写输出及scanf的小问题解决

1.博主在学习C语言时&#xff0c;也没太关注C语言的一些细节问题&#xff0c;导致后面有人问问题的时候一时没回答出来&#xff0c;也就是所谓的基础不牢地动山摇&#xff0c;比如这一次有同学问的scanf键盘输入的小问题&#xff0c;折腾了一阵子还是想出来问题所在。 2.废话不…

基于 Gin 的 HTTPS 代理 Demo

上次写了 基于 Gin 的 HTTP 代理 Demo 之后&#xff0c;对这方面还是蛮感兴趣的&#xff0c;所以就接着继续走下去。为了这个主题的内容&#xff0c;我斥巨资购入了一本二手的 《HTTP 权威指南》&#xff0c;因为我知道这本书里面有我想要的知识。在我还在大学的时候&#xff0…