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

在这里插入图片描述

今天文章的内容是关于我们如何利用堆的特性对我们的数组进行排序,还有就是我们的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.mzph.cn/news/174331.shtml

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

相关文章

【SQL Server2019SSMS】安装 | 卸载手册

目录 &#x1f4cb;前言 ⛳️【SQL Serverssms】安装 1. SQL Server自定义安装 2. SSMS安装 ⛳️【SQL Server】卸载 &#x1f4cb;前言 &#x1f308;个人主页&#xff1a;Sarapines Programmer &#x1f525; 系列专栏&#xff1a;本期文章收录在《宝藏工具使用手册》&am…

区块链介绍

区块链提供了比特币的公共账本&#xff0c;这是一个有序的、带有时间戳的交易记录。这个系统用于防止重复消费和修改之前的交易记录。 Introduction 比特币网络中的每个完全节点都独立存储只包含该节点验证的块的区块链。当多个节点在他们的区块链中都有相同的块时&#xff0…

00TDI 这件红色大衣也太适合过年穿了

分享女儿的时尚穿搭—红色大衣 这款大衣非常厚实 摸起来很软糯的触感 复合了660-700g绵羊绒 厚实度堪比一件厚实的羽绒服 门禁处做了立体的爱心装饰 精致又可爱&#xff01;&#xff01;&#xff01;

java--单继承、Object

java是单继承的&#xff0c;java中的类不支持多继承&#xff0c;但是支持多层继承。 反证法&#xff1a; 如果一个类同时继承两个类&#xff0c;然后两个类中都有同样的一个方法&#xff0c;哪当我创建这个类里的方法&#xff0c;是调用哪父类的方法 所以java中的类不支持多继…

【Linux】:信号的产生

信号 一.前台进程和后台进程1.前台进程2。后台进程3.总结 二.自定义信号动作接口三.信号的产生1.键盘组合键2.kill信号进程pid3.系统调用1.kill函数2.raise函数3.abort函数 四.异常五.软件条件六.通过终端按键产生信号 一.前台进程和后台进程 1.前台进程 一个简单的代码演示 …

【云备份】数据管理模块

文章目录 1. 数据管理模块要管理什么数据&#xff1f;2. 数据管理模块如何管理数据&#xff1f;3. 数据管理模块的具体实现BackupInfo 数据信息类NewBackupInfo —— 获取各项属性信息 DataManager 数据管理类构造函数析构函数insert —— 新增update —— 修改GetOneByURL——…

数据结构之时间复杂度与空间复杂度

1.算法效率 1.1 如何衡量一个算法的好坏&#xff1f; 比方说我们非常熟悉的斐波拉契数列&#xff1a; long long Fib(int N) {if(N < 3)return 1;return Fib(N-1) Fib(N-2); } 递归实现方式非常简洁&#xff0c;但一定好吗&#xff1f;如何衡量其好与坏&#xff1f; 1…

JVM——垃圾回收器(Serial,SerialOld,ParNew,CMS,Parallel Scavenge,Parallel Old)

目录 1.垃圾回收器的组合关系1.年轻代-Serial垃圾回收器2.老年代-SerialOld垃圾回收器3.年轻代-ParNew垃圾回收器4.老年代- CMS(Concurrent Mark Sweep)垃圾回收器CMS执行步骤&#xff1a;CMS垃圾回收器存在的问题缺点&#xff1a;CMS垃圾回收器存在的问题 – 线程资源争抢问题…

机器学习——支持向量机(SVM)

1.线性支持向量机 1.1数学模型 机器学习最终都是求解目标函数的最优问题&#xff1b; 一般都是讲问题转化为最小值来求解。 数学模型获得是一个不等式约束的最小化问题&#xff0c;求解时可通过构建拉格朗日函数求解。 1.2 拉格朗日函数及对偶问题求解 1.3 SMO算法求解 SMO算…

鸿蒙应用开发-初见:ArkTS

作者&#xff1a;HarderCoder ArkTS ArkTS围绕应用开发在 TypeScript &#xff08;简称TS&#xff09;生态基础上做了进一步扩展&#xff0c;继承了TS的所有特性&#xff0c;是TS的超集 ArkTS在TS的基础上扩展了struct和很多的装饰器以达到描述UI和状态管理的目的 基本语法 …

Redis Lua沙盒绕过 命令执行(CVE-2022-0543)漏洞复现

Redis Lua沙盒绕过 命令执行(CVE-2022-0543)漏洞复现 Redis如果在没有开启认证的情况下&#xff0c;可以导致任意用户在可以访问目标服务器的情况下未授权访问Redis以及读取Redis的数据。–那么这也就是redis未授权访问了 Redis的默认端口是6379 可以用空间测绘搜索&#xff…

【青蛙跳台阶问题 —— (三种算法)】

青蛙跳台阶问题 —— (三种算法&#xff09; 一.题目介绍1.1.题目1.2.图示 二.解题思路三.题解及其相关算法3.1.递归分治法3.2.动态规划算法&#xff08;Dynamic Programming&#xff09;3.3.斐波那契数列法 四.注意细节 一.题目介绍 1.1.题目 一只青蛙一次可以跳上1级台阶&am…

GWAS 分析模型 | FaST-LMM

GWAS 分析模型 | FaST-LMM FaST-LMM (Factored Spectrally Transformed Linear Mixed Models) 是一个用于进行全基因组关联分析&#xff08;GWAS&#xff09;的模型。与标准混合线性模型相比&#xff0c;FaST-LMM 通过对遗传相似性矩阵进行单次谱分解来减少计算资源消耗并提升运…

超全超实用行业解决方案合集,覆盖十大行业数据应用需求

现代企业面对复杂的业务需求&#xff0c;对数据分析的需求日益增加。 从实时销售到市场趋势&#xff0c;从客户行为到产品优化&#xff0c;每个环节都依赖于数据支持。然而&#xff0c;传统的数据分析平台常分散在不同系统和团队中&#xff0c;形成数据孤岛&#xff0c;降低了…

企业如何保障跨境金融业务中的数据安全传输?

随着全球化的不断深入&#xff0c;跨境金融业务日益频繁&#xff0c;然而在这些业务中&#xff0c;数据的安全传输一直是企业面临的重大挑战。跨境业务数据传输可能会遇到多种困难&#xff0c;如网络攻击、数据泄露、通信故障等。因此&#xff0c;企业需要采取有效的措施来确保…

C#,《小白学程序》第二十二课:大数的乘法(BigInteger Multiply)

1 文本格式 using System; using System.Linq; using System.Text; using System.Collections.Generic; /// <summary> /// 大数的&#xff08;加减乘除&#xff09;四则运算、阶乘运算 /// 乘法计算包括小学生算法、Karatsuba和Toom-Cook3算法 /// </summary> p…

漏洞复现--致远 M3 反序列化 mobile_portal RCE

免责声明&#xff1a; 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直…

AIGC系列之:DDPM原理解读(简单易懂版)

目录 DDPM基本原理 DDPM中的Unet模块 Unet模块介绍 Unet流程示意图 DownBlock和UpBlock MiddleBlock 文生图模型的一般公式 总结 本文部分内容参考文章&#xff1a;https://juejin.cn/post/7251391372394053691&#xff0c;https://zhuanlan.zhihu.com/p/563661713&…

03 项目运行

前面两篇文章对项目架构+源码架构做了分析,这篇文章先将服务部署一下,能够让大家有个直观的感受。 组件资源 项目运行的各种组件已经为你准备好了,有需要的直接百度云盘下载: 链接:https://pan.baidu.com/s/1hN6qf20gamMHPmA_qXwsLg提取码:o4k9MySQL数据库创建 找到的…

2023年最新Dev-C++下载安装以及C语言环境搭建教程(C语言入门)

文章目录 写在前面C语言简介Dev-C简介Dev-C下载安装Dev-C使用教程 写在后面 写在前面 2023年最新Dev-C下载安装以及C语言环境搭建教程&#xff0c;快来看看吧&#xff01; C语言简介 C语言是一种通用的高级程序设计语言&#xff0c;由美国计算机科学家Dennis Ritchie于20世纪…