堆是具备以下特征的一种特殊二叉树:
1、是一棵完全二叉树
2、每个节点的值均小于(大于)或等于它的两个孩子节点(如果存在)的值。
3、树根节点(称为堆顶元素)的值最小(称为小顶堆)或最大(成为大顶堆)。
所以,堆顶元素(即第一个元素)必为最大值(或最小值)。
堆树例子:
86
_____/ \_______
/ \
42 80
_/ \_ _/ \_
/ \ / \
18 32 51 25
/
12
堆排序步骤:
1、转换成完全二叉树
例:list_datas = [88, 6, 57, 72, 60, 42, 83, 73, 48, 85]
数组从左到右按二叉树层次遍历方式填充到二叉树中:
88
_____/ \_______
/ \
6 57
_/ \_ _/ \_
/ \ / \
72 60 42 83
/ \ /
73 48 85
但这里并不是要真的建立Node组成二叉树,用数组表示二叉树就行了。
由于是按一定规则填充到二叉树上的,那么在数组中,就会存在数学规律,
不难发现,每个数的左右孩子分别为 2i + 1、2i + 2, i 为 该数在数组中的下标值,
因此,不需要单独储存二叉树,仅使用数学公式就够了。
2、建堆(将完全二叉树调整为堆):对于有n个节点的完全二叉树,可按如下步骤,使其调整为堆:
① 从中间(位置 n/2 - 1,可以证明为最后一个非叶子节点)节点开始调整;
② 找出以此节点作为父节点的两个孩子节点的较小值(若建大顶堆则选较大值),并与父结点比较,
若小于(大于)父节点,则与父节点进行交换。然后,以交换后的子节点作为父节点。重复此步骤
直到没有了子节点(即叶子节点).
③ 把步骤②中原来父节点的位置往前推一个位置,作为新的父节点。重复步骤②,直到根节点为止,
此时完全二叉树将成为一个堆,根节点(即数组第一个值)则是最大值(或最小值)。
3、取堆顶,并用堆底代替堆顶
4、重复 2、3
演示完全二叉树建成小顶堆的过程:
[88, 6, 57, 72, 60, 42, 83, 73, 48, 85]
88
_____/ \_______
/ \
6 57
_/ \_ _/ \_
/ \ / \
72 60 42 83
/ \ /
73 48 85
INFO: n = 10, 取位置 i = (n // 2) - 1 = 4(即60,是最后一个非叶子节点)作为第一个节点。
INFO: 根据公式 2i + 1, 2i + 2 推算左右孩子节点,即9, 10,10 超过最大下标9,说明没有右孩子。
INFO: 60 < 85, 不需要调换顺序,i -= 1 = 3, 节点换到 72,
判断 72,73,48, 最小是48,需要把72,48调换顺序,
数组变成:[88, 6, 57, 48, 60, 42, 83, 72, 48, 85]
88
_____/ \_______
/ \
6 57
_/ \_ _/ \_
/ \ / \
48 60 42 83
/ \ /
73 72 85
INFO: 新节点变成交换后的子节点, i = 8, 值72,根据公式 2i + 1, 2i + 2 推算无左右孩子节点, i -= 1
INFO: i=7, 依然无左右孩子, i -= 1
INFO: i=6, i=5, 都无左右孩子,i -= 1
INFO: i 回到 4,但无需调整, i -= 1;
INFO: i 回到 3,但无需调整, i -= 1;
INFO: i=2,调整 42、57
数组变成:[88, 6, 42, 48, 60, 57, 83, 72, 48, 85]
88
_____/ \_______
/ \
6 42
_/ \_ _/ \_
/ \ / \
48 60 57 83
/ \ /
73 72 85
INFO: 继续重复操作后最后变成
[6, 48, 42, 72, 60, 57, 83, 73, 88, 85]
6
_____/ \_______
/ \
48 42
_/ \_ _/ \_
/ \ / \
72 60 57 83
/ \ /
73 88 85
INFO: 完成最小堆建立,输出栈顶(6),并把栈底(85)调换到栈顶处:
6
[85, 48, 42, 72, 60, 57, 83, 73, 88]
85
_____/ \_______
/ \
48 42
_/ \_ _/ \_
/ \ / \
72 60 57 83
/ \
73 88
非递归实现
def heap_sort_iteration4(datas):
end_index = len(datas) - 1
while end_index > 0:
i = (end_index + 1) // 2 - 1
while i >= 0:
left, right = 2 * i + 1, 2 * i + 2
max_i = max(i, left, right if right <= end_index else i, key=lambda _i: datas[_i])
if max_i != i:
datas[i], datas[max_i] = datas[max_i], datas[i]
i -= 1
datas[0], datas[end_index] = datas[end_index], datas[0]
end_index -= 1
return datas
递归实现:
def heap_sort_recursion4(datas):
def _bulid_large_heap(end_index):
if end_index == 0:
return
i = (end_index + 1) // 2 - 1
while i >= 0:
left, right = 2 * i + 1, 2 * i + 2
max_i = max(i, left, right if right <= end_index else i, key=lambda _i: datas[_i])
if max_i != i:
datas[i], datas[max_i] = datas[max_i], datas[i]
i -= 1
datas[0], datas[end_index] = datas[end_index], datas[0]
_bulid_large_heap(end_index - 1)
_bulid_large_heap(len(datas) - 1)
return datas