最大堆:
最大堆的任何一个父节点的值,都大于或等于它左孩子或右孩子节点的值
最小堆:
任何一个父节点的值都小于或等于它左孩子或右孩子节点的值
二叉堆的根节点叫做堆顶
最大堆和最小堆的特点决定了,最大堆的堆顶是整个堆中的最大元素;
最小堆的堆顶是整个堆中的最小元素
二叉堆的自我调整
就是把一个不符合堆性质的完全二叉树,调整成一个堆
有如下几种操作:
1.插入节点:上浮(插入位置是完全二叉树的最后一个位置,也就是最新的一个叶子节点(叶子节点插入从左到右))
2.删除节点:下沉(删出是处于堆顶的节点,然后把堆的最后一个节点临时补到原本堆顶的位置,让暂时处于堆顶位置的节点与左右孩子节点进行比较)
3.构建二叉堆:本质是让所有非叶子节点依次"下沉"(非叶子节点:有孩子节点的节点;相对的,叶子节点:没有孩子节点的节点)
如果该节点大于它的左孩子、右孩子节点中最小的一个,则该节点下沉。
时间复杂度
堆的插入和删除操作,时间复杂度是O(logn),这两个操作都是在单一节点上实现"上浮"或下沉,这两个操作的平均交换次数都是堆高度的一半
而构建堆是所有非叶子节点依次"下沉",时间复杂度是O(n),而不是O(nlogn)
二叉堆用数组来表示
二叉堆虽然是完全二叉树,但它的存储方式并不是链式存储,而是顺序存储。获句话说,二叉堆的所有节点都存储在数组中。
在数组中没有左指针和右指针,可以依靠数组下标来计算
假设父节点的下标是parent,那么它的左孩子下标就是2parent+1;右孩子的下标就是2parent+2.
下方展示的代码其实我看不懂
def up_adjust(array=[]):
"""
二叉堆的尾节点上浮操作
:param array: 原数组
"""
child_index = len(array) - 1
parent_index = (child_index - 1) // 2
# temp保存插入的叶子节点值,用于最后的赋值
temp = array[child_index]
while child_index > 0 and temp < array[parent_index]:
# 无需真正交换,单向赋值即可
array[child_index] = array[parent_index]
child_index = parent_index
parent_index = (parent_index - 1) // 2
array[child_index] = temp
'''
'''
def down_adjust(parent_index, length, array=[]):
"""
二叉堆的节点下沉操作
:param parent_index: 待下沉的节点下标
:param length: 堆的长度范围
:param array: 原数组
"""
# temp保存父节点值,用于最后的赋值
temp = array[parent_index]
child_index = 2 * parent_index + 1
while child_index < length:
# 如果有右孩子,且右孩子小于左孩子的值,则定位到右孩子
if child_index + 1 < length and array[child_index + 1] < array[child_index]:
child_index += 1
# 如果父节点小于任何一个孩子的值,直接跳出
if temp <= array[child_index]:
break
# 无需真正交换,单向赋值即可
array[parent_index] = array[child_index]
parent_index = child_index
child_index = 2 * child_index + 1
array[parent_index] = temp
def build_heap(array=[]):
"""
二叉堆的构建操作
:param array: 原数组
"""
# 从最后一个非叶子节点开始,依次下沉调整
for i in range((len(array)-2) // 2, -1, -1):
down_adjust(i, len(array), array)
my_array = list([1, 3, 2, 6, 5, 7, 8, 9, 10, 0])
up_adjust(my_array)
print(my_array)
my_array = list([7, 1, 3, 10, 5, 2, 8, 9, 6])
build_heap(my_array)
print(my_array)