标签:最大值 接下来 str heap length 优先 转换 算法 完全
首先介绍优先队列:
Heap(堆)是对优先队列的一种实现
每个元素都有对应的key值,且对于大顶堆(Max Heap)而言,其每个元素的key值都 ≥ 子元素的key值
堆可以可视化为一棵完全二叉树:
堆对应的树具有如下性质:(这里层数从0开始)
别忘了之前讲过的完全二叉树的性质:n个结点的完全二叉树高度为 lgn
max_heapify() 只针对具有 “简单错位” 的堆进行调整,single violation是这里特别强调的一个前提条件,详见下
注意这里有个重要假设:
那么 max_heapify() 的职责就很显然了:将最上面的结点 i 下沉到正确的位置。
显然影响运行时长的因素在于 i 子树的层数,假设 i 子树深为 lgn,那么max_heapify() 的时间复杂度为O(lgn)
我们已经知道 max_heapify() 严格要求了左右子树必须为大顶堆,那么对于一个完全乱序的数组A,显然直接调用max_heapify(A)是不允许的,这时要如何将其转换为大顶堆呢?
Build_Max_Heap(A)将通过多次调用 max_heapify() 将输入A 转换成一个大顶堆:
由于叶子结点没有child,其本身已经是一个大顶堆,满足 single violation
所以我们可以跳过最下层的叶子,从 level 1 开始调用 max_heapify()
(level 1 为叶子结点之上的一层,即倒数第二层)
wait a moment:这里解释一下为什么要从下往上处理:
前面提到了使用 max_heapify() 的前提条件是左右子树已经为大顶堆,因此在倒数第二层调用显然是符合要求的(因为level 1中所有结点的左右子树均已经为大顶堆)
那么在这一层处理完毕后,所有“从level 1长出的子树均被调整为了大顶堆”,也就满足了接下来在level 2中调用max_heapify()的前置条件,以此类推...
接下来继续 Build_Max_Heap(A)的算法思路
我们已经解释了为何循环要从下至上处理,
即 for n/2 downto 1:
不难看出,对于level 1的所有结点,最坏情况下的调整时间也只是O(1)
随着level向上增高,对level L层的结点进行max_heapify操作需要O(L)时间
最上层只有一个根结点,对它的调整需要O(lgn) ---(Tips: 复杂度源于)
将各层所需调整时间加起来可以得到整个算法的时间复杂度为O(n) ,步骤如下:
(括号内为一个收敛级数且收敛为常数)
给出一个乱序数组 A,通过 Build_Max_Heap(A)将其转化为大顶堆的步骤分解如下:
思路步骤:
时间复杂度:
整个算法的开头需要调用一次建立Build-Max-Heap(A) , --- O(n)
此后开始n次迭代: (第一次迭代确定了最大值,第二次确定次大值, ... ,n个数排序完毕需要迭代n次)
每次迭代包括一次swap操作和max_heapify() ---O(lgn)
因此总的时间复杂度为 O(n + nlgn) = O(nlgn)
标签:最大值 接下来 str heap length 优先 转换 算法 完全
原文地址:https://www.cnblogs.com/potofsalt/p/14773949.html