上一节我们讲过从数组到堆的变化过程,如何建立大根堆,如何维护大根堆,本节我们讲一讲如何利用上一节的维护堆进行堆排序。建堆和维护堆见大根堆的建立和节点维护。
1. 思路
回忆上一节我们利用buildMaxHeap(int nums[])
函数建立了一个大根堆。大根堆的性质为:所有子树的根均不比孩子小。所以,大根堆A
的最大元素一定存储在A[1]
中(A
不为空且堆元素从A[1]
处开始存储)。
我们可以利用这一性质,设堆为A[1......n]
,A[1]
元素为最大,将A[1]
和A[n]
交换,并将节点n
从堆中移除,此时堆变为A[1......n-1]
。交换并移除节点后,堆除了交换的那个根节点外,其余孩子节点仍满足大根堆的性质。为了使得A[1......n-1]
仍为大根堆,我们调用维护堆的函数堆根节点(即A[1]
),为maxHeapIfy(A, 1)
。
进行维护后,A[1......n-1]
又成为了一个大根堆。之后重复这个交换—>移除–>维护的过程,直至堆的大小降为2,原先的堆A[1......n]
就得到了一个升序的数组了。
2. 伪代码
HeapSort(A):
#首先根据线性表A建大根堆
buildMaxHeap(A)
#操作从最后一个元素开始一直到第二个元素
for i = A.length downto 2
exchange A[1] and A[i] #对应上文的交换过程
A.heap-size = A.heap-size-1 #对应上文移除过程
maxHeapIfy(A,1) #对应上文维护A[1]节点
3. 时间复杂度
由上文伪代码可以看出,HeapSort(A)
过程调用了一次buildMaxHeap(A)
过程,调用了n-1次maxHeapIfy(A,1)
。上一节我们已经简要分析了每次调用maxHeapIfy(A,1)
过程的时间复杂度为
O
(
lg
n
)
O(\lg n)
O(lgn),每次调用buildMaxHeap(A)
过程的时间复杂度为
O
(
lg
n
)
O(\lg n)
O(lgn)。
所以运行时间为:
O
(
(
n
−
1
)
lg
n
)
+
O
(
n
)
O((n-1)\lg n)+O(n)
O((n−1)lgn)+O(n)
时间复杂度可以简化为:
O
(
n
lg
n
)
O(n\lg n)
O(nlgn)
4. C++实现
困喽,睡觉,想起来再写吧🥱😢🤦♂️🤣😂😞😒👍🙌😉🤷♂️🤷♀️🤩🫡🤨😐😑😶🫥😥😴🥱😪