堆
- 定义:堆是一个数组,它可以看成一个近似的完全二叉树,树上的每个节点对应数组中的一个元素,除了最底层外,该数是完全充满的,而且是从左向右充满,如下图所示:
- 基本性质:给定一个节点的下标 i i i,则它的父节点 ⌊ i / 2 ⌋ \lfloor i/2 \rfloor ⌊i/2⌋,左孩子: 2 i 2i 2i,右孩子: 2 i + 1 2i+1 2i+1
- 最大堆: A [ p a r e n t ( i ) ] ≥ A [ i ] A[parent(i)] \ge A[i] A[parent(i)]≥A[i],在任一子树中,该子树所包含的所有节点的值都不大于该子树根节点值,堆中的最大元素存放在根节点中
- 最小堆: A [ p a r e n t ( i ) ] ≤ A [ i ] A[parent(i)] \le A[i] A[parent(i)]≤A[i],在任一子树中,该子树所包含的所有节点的值都不小于该子树根节点值,堆中的最小元素存放在根节点中
堆的性质
最大堆保证其根节点
A
[
i
]
≥
A
[
l
e
f
t
(
i
)
]
A[i] \ge A[left(i)]
A[i]≥A[left(i)] 且
A
[
i
]
≥
A
[
r
i
g
h
t
(
i
)
]
A[i] \ge A[right(i)]
A[i]≥A[right(i)],下图为在第
i
=
2
i=2
i=2的节点,保证最大堆性质移动操作步骤:
代码实现如下:
# 维持最大堆的性质
def max_heapify(A, i, size):
# 节点i的左孩子, 由于python 索引是从0开始,不是从1开始,所以左 2*i --> 2*i+1 右 2*i+1 --> 2*i+2
l = 2*i+1
# 节点i的右孩子
r = 2*i + 2
#size = len(A)
size = size
if l<size and A[l]>A[i]:
largest = l
else:
largest = i
if r<size and A[r]>A[largest]:
largest = r
if largest!=i:
temp = A[i]
A[i] = A[largest]
A[largest] = temp
# 以largest为根节点的子树,可能又违背最大堆性质,所以需要继续递归调用
max_heapify(A, largest, size)
建堆
用自底向上的方法,利用上述维持堆的性质,把长度为
n
n
n的数组
A
A
A转换成最大堆,根据堆的性质,
⌊
n
/
2
⌋
+
1
,
.
.
,
n
\lfloor n/2 \rfloor +1,..,n
⌊n/2⌋+1,..,n是叶子节点,所以,只需要对其他非叶子节点调用max_heapify,图过程如下:
代码块:
#建堆,自顶向下方法,把一个大小为n的数组A,转换为最大堆
def build_max_heap(A):
size = len(A)
mid = int(size/2) - 1
#从堆的性质得出 A[mid+1..size]都是树的叶节点,所以只需要对其他节点调用max_heapify,保证最大堆的性质就可
for i in range(mid,-1,-1):
max_heapify(A,i, size)
return A
堆排序算法
因为数组中最大元素总在第一个元素根节点 A [ 1 ] A[1] A[1]中,通过把它与 A [ n ] A[n] A[n]交换,同时从堆中去掉节点 n n n,新交换的节点可能不能满足最大堆性质,所以每次需要重新调用max_heapify函数,堆排序算法复杂度为 O ( n l g n ) O(nlgn) O(nlgn),整个流程代码如下:
# -*-coding:utf8 -*-
import sys
# 维持最大堆的性质
def max_heapify(A, i, size):
# 节点i的左孩子, 由于python 索引是从0开始,不是从1开始,所以左 2*i --> 2*i+1 右 2*i+1 --> 2*i+2
l = 2*i+1
# 节点i的右孩子
r = 2*i + 2
#size = len(A)
size = size
if l<size and A[l]>A[i]:
largest = l
else:
largest = i
if r<size and A[r]>A[largest]:
largest = r
if largest!=i:
temp = A[i]
A[i] = A[largest]
A[largest] = temp
# 以largest为根节点的子树,可能又违背最大堆性质,所以需要继续递归调用
max_heapify(A, largest, size)
#建堆,自顶向下方法,把一个大小为n的数组A,转换为最大堆
def build_max_heap(A):
size = len(A)
mid = int(size/2) - 1
#从堆的性质得出 A[mid+1..size]都是树的叶节点,所以只需要对其他节点调用max_heapify,保证最大堆的性质就可
for i in range(mid,-1,-1):
max_heapify(A,i, size)
return A
def heap_sort(A):
#建堆
build_max_heap(A)
print(A)
length = len(A)
#最大元素总在根节点A[0],通过与最后一个节点交换A[length-1],新的节点需要维持最大堆性质,同时每次去掉交换后的最后一个节点
for i in range(length-1,0,-1):
# chage A[0] with A[i]
temp = A[i]
A[i] = A[0]
A[0] = temp
max_heapify(A,0, i)
return A
if __name__=='__main__':
A = [4,1,3,2,16,9,10,14,8,7]
print(A)
A = heap_sort(A)
print(A)