堆排序建堆复杂度为O(n)的证…

为什么堆化 heapify() 只用 O(n) 就做到了? heapify() 前面两篇文章介绍了什么是堆以及堆的两个基本操作,但其实呢,堆还有一个大名鼎鼎的非常重要的操作,就是 heapify() 了,它是一个很神奇的操作, 可以用 O(n) 的时间把一个乱序的数组变成一个 heap。 但是呢,heapify() 并不是一个 public API,看: 所以我们没有办法直接使用。 唯一使用 heapify() 的方式呢,就是使用 PriorityQueue(Collection<? extends E> c) 这个 constructor 的时候,人 阅读详情

今天重温堆排序,在网上搜了好多博客文章,都是泛泛而谈。有的只讲了思路,有的直接贴上一份或几份代码。好一点的对复杂度进行了分析,但是讲到建堆复杂度,就一笔带过或者说请参考算法导论××页。我觉得求建堆复杂度并不难,了解一下对于理解堆排序是有好处的,下文为求解过程。

 

堆排序就是借助于堆的数据结构和堆的操作函数来完成排序功能的过程。堆的数据结构可以借助于数组表示出来并可以高效地进行堆的操作。我们为堆(最大堆)的元素从从上到下(从根到叶),从左到右进行1到n的编号,对应到数组的相应Index。为了方便对应,这里数组的0位置空了出来。定义几个操作:

 

 

  1. PARENT(i) : i/2 
  2. LEFT(i) : i<<1
  3. RIGHT(i) : (i<<1)+1

 

以上三个操作的意义很简单,是属于堆操作中的函数。还有两个函数HEAPIFY , BUILD-HEAP,它们分别包装了一种特殊的堆修正操作和初始化建堆操作。其中HEAPIFY是在i的左右子树都是堆的前提下对以i为根的树进行修正堆的操作。

 

用以上几个堆的包装函数就可以完成堆排序函数HEAP-SORT。
现在来分析建堆过程BUILD-HEAP:
[cpp]  view plain copy
  1. BUILD-HEAP(int A[]){  
  2.     heapsize = A.length-1;  
  3.     for(int i=headsize/2;i>=1;i--){  
  4.         HEAPIFY(A,i);  
  5.     }  
  6. }  
可以发现我们还需要分析一下HEAPIFY:
[cpp]  view plain copy
  1. HEAPIFY(int A[],int i){  
  2.     int l = LEFT(i);  
  3.     int r = l + 1;  
  4.     int largest = i;  
  5.     if(l<=heapsize&&A[l]>A[i]){  
  6.         largest = l;  
  7.     }  
  8.     if(r<=heapsize&&A[r]>A[largest]){  
  9.         largest = r;  
  10.     }  
  11.     if(largest != i){  
  12.         swap(i,largest);  
  13.         HEAPIFY(A,largest);  
  14.     }  
  15. }  

HEAPIFY对一层的比对交换所需时间是常数级的O(1),然后进入递归过程。设堆共有N个节点,则高度最多为LgN,因此HEAPIFY最多递归LgN,耗费时间O(LgN)。

再看BUILD-HEAP,循环HEAPIFY了N/2次,因此复杂度的上界很好理解,为(N/2)*LgN,即O(NLgN)。

但是这并不是一个紧绷的复杂度,仔细想想也知道根本没进行(N/2)*LgN那么多次。

所有的叶节点都不进行HEAPIFY,HEAPIFY是从高度为1的节点开始进行直到根为止。这时候我们需要理解HEAPIFY的执行过程,而不能单纯的理解为LgN。对于高度为1的节点,至多替换发生1次。对于高度为2的节点,至多替换发生2次,以此类推,对于高度为h的节点,至多发生替换h次。我们知道,堆是满树,叶节点共有N/2个,它们的高度是0 。高度为1的节点正是他们的父节点,共有(N/2)/2个。高度为2的,类推有((N/2)/2)/2个。因此高度为h的共有N/(2的(h+1)次方)个。

 

 

好了,堆的高度总共只有0到LgN,现在每个高度的节点个数清楚,每个高度的每个节点至多发生的替换次数也清楚,则总共发生的替换数也就清楚了:

(N/(2的(h+1)次方)) * h 的求和     (h取值0~LgN)

N是常数, 化一下变成(N/2) * ( h / (2的h次方) ) (h取值0~LgN)
接下来就是一个级数求和问题了,学过高数的都应该知道怎么求。求 ( h / (2的h次方) ) (h取值0~LgN):
设结果为S,则S = 1/2 + 2/(2的2次方)  + 3/(2的3次方) ... + LgN/ (2的LgN次方)。另S*(1/2) = 1/(2的2次方) + 2/(2的3次方) + 3/(2的4次方)...+LgN/(2的(LgN+1)次方)。
两式错位相减 有 S*(1/2) = 1/2 + 1/(2的2次方)  + 1/(2的3次方) ... + 1/ (2的LgN次方) - LgN/(2的(LgN+1)次方)。
右式前边几项为等比数列,最终化简结果为 S = 2 - (1/2)的(LgN-1)次方-LgN / ( 2的LgN次方)。
当N趋向于无穷大时,右式的二,三两项都趋近于0,于是limS = 2。所以我们要求的BUILD-HEAP复杂度为O( (N/2) * S ) = O(N)。

从上述推导过程可以看出,重点在于根据BUILD-HEAP过程找出计算复杂度的算式,然后利用求级数,求极限的方法解出结果。其实最终还是回归了理解算法和合理利用数学工具上。

[建堆&堆排序的时间复杂度推导]向上建堆&向下建堆&堆排序的时间复杂度分析推导 这样的二叉树的节点个数为NNN= 2^h-1高度为hlog2Nh=log_2Nhlog2​N。 阅读详情

相关推荐

面试考点--堆(向下调整算法,向上调整算法,建堆,堆排序)以及堆排序、建堆的时间复杂度分析(图文并茂)

堆 堆是一种完全二叉树,现实中通常使用顺序结构的数组来存储堆,此处的堆是一种数据结构(完全二叉树),而不是虚拟进程空间中的那个堆(是操作系统用来管理内存的一个区域分段 )

zw的博客 3108

堆排序建堆复杂度为O(n)的证明

今天重温堆排序,在网上搜了好多博客文章,都是泛泛而谈。有的只讲了思路,有的直接贴上一份或几份代码。好一点的对复杂度进行了分析,但是讲到建堆复杂度,就一笔带过或者说请参考算法导论××页。我觉得求建堆复杂度并不难,了解一下对于理解堆排序是有好处的,下文为求解过程。 堆排序就是借助于堆的数据结构和堆的操作函数来完成排序功能的过程。堆的数据结构可以借助于数组表示出来并可以高效地进行堆的操作。我们为堆

mtawaken的专栏 1万+

【树与二叉树】堆的时间复杂度详解以及堆的应用—堆排序、TOP - K问题

今天我们认识并学习了堆的时间复杂度,并且通过分析对向下调整算法和向上调整算法也有了更深入的了解。还对堆的应用——堆排序、TOP - K问题进行了分析。下一篇博客我们将对二叉树链式结构进行分析及实现。希望我的文章和讲解能对大家的学习提供一些帮助。

Sherry的博客 4149

时间复杂度为O(N)的建堆方法分析+代码

建堆时间复杂度O(N)分析和堆排序TOPK问题

myscratch的博客 1962

建堆与堆排序详细解析

1.堆的特性 堆有大根堆和小根堆,大根堆的子树,父结点比子结点都要大,小跟堆的子树,父节点都要比子结点小。 堆有这样的特性: n个元素的堆的高度是lognlognlogn,因为堆是一棵完全二叉树或者近似一棵完全二叉树。 堆中,如果父节点的index是n,那么这个结点的左右子结点的index 分别是2∗index+1​2*index+1​2∗index+1​和2∗index+2​2*index +...

慢慢变强大 4480

关于堆排序建堆时间以及堆排序的分析之暑假学习记录

近日总结了一系列的算法排序算法,在理解堆排序的时候遇到了一点阻碍,那就是建堆时间复杂度。本文先详细分析建堆时间复杂度,然后贴出整体堆排序思想和代码: 1. 建堆时间复杂度分析: 刚开始想建堆的时候,总认为这个时间复杂度是O(nlogn),这个当然并非是错的,因为这个是big-O时间。但是还有更精确的时间复杂度就是O(n). 首先说说O(nlogn)是怎么来的。 (1) 最大堆的调整 每次得

usc_su的专栏 8550

为什么建堆的时间复杂度是O(n)?

如果仅从代码上直观观察,会得出构造二叉堆的时间复杂度为O(n㏒n)的结果,这个结果是错的,虽然该算法外层套一个n次循环,而内层套一个分治策略下的㏒n复杂度的循环,该思考方法犯了一个原则性错误,那就是构建二叉堆是自下而上的构建,每一层的最大纵深总是小于等于树的深度的,因此,该问题是叠加问题,而非递归问题。那么换个方式,假如我们自上而下建立二叉堆,那么插入每个节点都和树的深度有关,并且都是不断的把树折半

LeoSha的专栏 2万+

堆排序的时间复杂度解析:为什么建堆只要O(N)?

本文深入解析了堆排序中线性时间建堆的数学原理。通过对比自上而下插入法和自底向上堆化法,重点剖析了基于向下调整算法的自底向上建堆策略,并利用数学推导证明了其时间复杂度为O(N),揭示了其高效性的本质。

weixin_29268331的博客 245

详解“为何建堆的时间复杂度O(N)?“+“排升序为什么不能建小堆?”

我们先来回顾一下堆排序:(需要有堆相关内容基础,详情可见我的上一篇文章(33条消息) “堆的增删查改“以及“堆排序“详解_beyond.myself的博客-优快云博客) 对于一个数组int a[ ],我们对他进行堆排序的操作有2种操作: ①新创建一个堆 hp ,把数组a中的数据通过"堆添加函数" HeapPush 一个一个放到堆的数组中,因为 堆添加函数 内部自带 向上调整算法AdjustUp ,因此会自动把插入的数据进行排序成小根堆,再利用 HeapTop找堆顶数据函数把堆顶数据(最小的数据)赋给..

zhang_si_hang的博客 3006

杰卡德距离(Jaccard Distance)原理与应用(Python和NLTK实现)

杰卡德距离(Jaccard Distance)是用来衡量两个集合差异性的一种指标,它是杰卡德相似系数的补集,被定义为1减去Jaccard相似系数。而杰卡德相似系数(Jaccard similarity coefficient),也称杰卡德指数(Jaccard Index),是用来衡量两个集合相似度的一种指标。杰卡德指数最早由瑞士苏黎世联邦理工学院的植物学和植物生理学教授保罗·杰卡德(Paul Jaccard)提出

白马负金羁 1万+

【理解机器学习算法】之Clustering算法(Agglomerative Clustering)

是一种层次聚类算法,通过逐步合并或“聚集”它们来构建嵌套聚类。这种方法采用自底向上的方式构建聚类层次:它从将每个数据点作为单个聚类开始,然后迭代合并最接近的聚类对,直到所有数据点合并为一个聚类,或直到达到指定的聚类数量。优点:- 聚合聚类在距离和连接准则的选择上具有多样性,使其适用于各种数据集。- 它产生了一个层次结构,这对于不同规模的聚类数据结构是有信息量的。-缺点:- 对于大数据集来说,它可能计算成本高,因为它需要在每次迭代中计算和更新所有聚类对之间的距离。

Hyman Qiu 2688

【数据结构】建堆算法复杂度分析及TOP-K问题

哈喽,各位小伙伴大家好!上期我们讲了堆排序和建堆算法。今天我们就来分析一下他们的时间复杂度。话不多说,咱们进入正题。向大厂冲锋!

大白的编程日记. 2256

传输线的特征阻抗

信号源内阻与所接传输线的特性阻抗大小相等且相位相同,或传输线的特性阻抗与所接负载阻抗的大小相等且相位相同,分别称为传输线的输入端或输出端处于阻抗匹配状态,简称为阻抗匹配。否则,便称为阻抗失配。有时也直接叫做匹配或失配。指一个电路输入端的等效阻抗。在输入端上加上一个电压源U,测量输入端的电流I,则输入阻抗Rin就是U/I。可以把输入端想象成一个电阻的两端,这个电阻的阻值,就是输入阻抗。- 对于电压驱动的电路,输入阻抗越大,则对电压源的负载就越轻,因而就越容易驱动,也不会对信号源有影响;

学海无涯的专栏 4465

对于堆排序的从下到上的建堆过程为什么时间复杂度是O(n)而不是O(nlogn)

堆排序的第一步:建堆过程为什么时间度是O(n)。 以升序为例,我们知道对于堆排序的第一步,是要先建立一个大顶堆。代码如下: void adjustHeap(vector<int>& arr, int parent, int length){ //先进行暂存最初的调整的开始位置即顶端的值 //如下也可以不用每次交换 int temp = arr[paren...

zhpf225的博客 2759

inferCNVpy分析流程学习

inferCNV的用途大家都已经很清楚了,接下来就走一下流程,试一试Python版的inferCNV。使用的数据集为:GSE188711,既往推文可见:Scanpy分析全流程(含harmonypy整合/细胞周期矫正/双细胞检测及去除)(

zfyyzhys的博客 1296

ubuntu20 安装px4、mavros、QGroundControl

首先安装一下ant,否则没法运行,同时因20.04的缺少动态库libawt_xawt.so无法被Java8给调用,我的解决办法是去18.04把18.04的相同路径下的动态库libawt_xawt.so移动到20.04的/usr/lib/jvm/java-11-openjdk-amd64/lib/路径中。用rosdepc的时候,你得确保你用的是鱼香ROS的提供的ROS安装方法,否则是没有rosdepc的。make px4_sitl gazebo之前,可以安装一下gazebo的 模型。二、安装MAVROS。

qq_58060770的博客 3349

如何建堆?

我们首先将数组原地建成一个堆。所谓“原地”就是,不借助另一个数组,就在原数组上操作。建堆的过程,有两种思路。 第一种是借助我们前面讲的,在堆中插入一个元素的思路。尽管数组中包含 n 个数据,但是我们可以假设,起初堆中只包含一个数据,就是下标为 1 的数据。然后,我们调用前面讲的插入操作,将下标从 2 到 n 的数据依次插入到堆中。这样我们就将包含 n 个数据的数组,组织成了堆。 第二种实现思路,跟第一种截然相反,也是我这里要详细讲的。第一种建堆思路的处理过程是从前往后处理数组数据,并且每个数据插入堆中时,都

鸭梨的博客 1万+

一文学会VOS2009对接网关指定走某个落地网关

介绍:  在我们日常使用VOS2009/3000的过程中,肯定会有很多落地网关与对接网关,并且某些时候还需要某些对接网关指定去走某个落地网关或者某些落地网关,从而实现比较精准的控制对接网关与落地网关的关系,下面就演示下在VOS2009/3000中如何配置某个对接网关指定走某个或者多个落地网关。 实现步骤:  VOS2009可以通过添加固定前缀的方式来指定对接网关走某个落地网关。 比如:  r1对接网关在被叫改写规则里面填写*:a*  r2对接网关在被叫改写规则里填写*:b* 1.登录VOS客户端 2.进入

非著名运维的博客 1963

堆排序:为什么说 O(N) 建堆才是降维打击?

你还在为 O(NlogN) 的建堆速度而烦恼吗? 本文将揭示堆排序中一个被忽视的性能秘密:通过高效的向下调整算法,建堆过程可以实现从 O(NlogN) 到惊人的 O(N) 提速!我们将对比分析两种建堆方法的复杂度差异,彻底理解 O(NlogN) 堆排序的运作原理。更重要的是,你将学会如何利用这一高效结构,以 O(NlogK) 的最优复杂度,轻松解决工程和面试中最常考的 TopK 数据筛选难题!

persevereX的博客 766

C++11、C++14 和 C++17 标准新特性详解

特性C++11C++14C++17auto类型推导✅范围 for 循环✅Lambda 表达式✅✅✅智能指针✅移动语义✅nullptr✅统一初始化{}✅✅constexpr✅✅✅✅数字分隔符✅结构化绑定✅✅✅文件系统库✅。

h a 6 6 6 c k 为 你解决复杂难题! 441

手把手教你学Simulink——基于Simulink的多用户MIMO系统仿真建模示例

多用户MIMO概述在多用户MIMO系统中,基站配备多个天线,可以同时与多个用户进行通信。利用空间复用技术,不同用户的信号被分配到不同的空间流上,从而实现频谱资源的高效利用。多用户MIMO的核心挑战包括信道估计、波束成形设计以及干扰管理。关键技术点波束成形:通过调整发射信号的相位和幅度,在特定方向上增强信号强度并减少干扰。信道估计:获取每个用户的信道状态信息(CSI),用于优化波束成形矩阵。调制与解调:选择合适的调制方式(如QPSK、16-QAM等)以适应不同用户的信道条件。干扰消除。

MHD0815的博客 271

堆的时间复杂度分析

总结点个数N=2^0+2^1+...+2^(h-1)=2^h-1。那么就可以得出高度和节点个数的关系h=log(N+1)。最少情况下,N=2^0+2^1+...+2^(h-2)+1=2^(h-1),同样高度和节点个数的关系:h=logN+1;相减得:F(h)=2^(h-1)+2^1+2^2+...+2^(h-2)-2^0*(h-1)最多需要调整的次数:T(h)=2^1*1+2^2*2+...+2^(h-1)*(h-1)相减得:T(h)=2^h*(h-1)-2^1-(2^2+2^3+...+2^(h-1))

2401_82677021的博客 1508

ETAS工具导入Com Arxml修改步骤

通讯协议栈开发一般通过导入DBC实现,ETAS工具本身导入DBC也是生成arxml后执行cfggen,本文介绍直接导入客户提供的arxml文件,实现通讯协议栈的生成。通过Arxml导入相比于DBC来说,少了从DBC转为arxml的过程。而且arxml中能提供的信息也会更多。如果是DBC的话,很多信息(如PNC的映射,CANTP的配置等)都需要额外配置。不过还有连DBC都无法提供的,那就更麻烦了~

赞的博客 1541

【堆排】为何使用向下调整法建堆比向上调整法建堆更好呢?

在博主的上一篇博客堆排(链接在这里点击即可)的总结中提出啦使用向上调整法建堆比使用向下调整法建堆更好,是因为使用向上调整法建堆的时间复杂度为O(n),使用向下调整法建堆的时间复杂度为O(n*logn)。接下来博主就教大家如何计算它们的时间复杂度。通过这篇博客相信柚柚们已经清楚向下调整法建堆和向上调整法建堆的时间复杂度怎么计算啦,后期博主还会更新有关数据结构的博客,感兴趣的柚柚们可以关注博主喔~

2301_80085602的博客 1883

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

[ 数据结构- C语言 ] 堆排序的优化算法

在浏览本篇博文的小伙伴可先浅看一下上篇堆和堆排序的思想: 戳这里可跳转上篇博文~~ 到这里的老铁相比对堆和堆排序有了简单的了解,那么下面我们对之前所写的堆排序进行优化~ Let's go! 目录 1.堆排序优化算法 1.1建堆的时间复杂度 1.1.1 向下调整建堆:O(N) 1.1.2向上调整建堆:O(N*logN) ​ 1.2堆排序的复杂度 1.2.1堆排序的时间复杂度 1.2.2堆排序的空间复杂度 1.3堆排序优化算法的复杂度 1.3.1 堆排序优化算法的时间复杂度 ..

优快云博客 4489

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

【数据结构】向上调整建堆和向下调整建堆的天壤之别以及堆排序算法

我们仍旧按最坏的情况来算。 最后一层的每个节点都需要向上调整h-1次,光最后一层调整的次数就已经有2^(h-1)*(h-1)次了。 光看这一层可以看出差距。 上一条讲的向下调整的特点是节点多的层级调整的次数少,是少乘多。 而现在讲的向上调整恰恰相反。 节点多的层级调整的次数多,是多乘多,这就造成了时间复杂度的巨大差异。 同样来计算一下。 假设高度为h。 F(h)=2^1*1+2^2*2+……+2^(h-2)*h-2+2^(h-1)*(h-1) 同样使用错位相减,解得F(h) = 2^h

陈大大陈的博客 1641

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

上一篇: 最大堆/最小堆
下一篇: explicit构造函数
wakeupwakeup
博客等级 码龄11年 9粉丝 49原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值