堆
堆结构非常重要
优先级列表
把一个数组理解成一个完全二叉树(脑补)
- 堆结构的heapInsert与heapify
- 堆结构的增大和减少
- 如果只是建立堆的过程,时间复杂度为O(N)
- 优先级队列结构,就是堆结构
二叉树
在不越界的情况下,第 i个元素:
- 左节点:2 * i + 1
- 右节点:2 * i + 2
- 父节点:(i - 1) / 2(0号元素的父节点还是0,-1/2 = 0)
完全二叉树
- 满二叉树:任何一个非叶节点,一定有左节点和右节点
- 不是满二叉树:所有节点,
从左到右依次补齐
大根堆
任何一棵子树的最大值,都是这棵子树的头部
小根堆
任何一棵子树的最小值,都是这棵子树的头部
生成堆
headInsert
一个新节点加入堆中,同时这个节点向上调整的过程
- 新进来一个节点,它只可能和H个元素比较:H==>
完全二叉树的高度:O(logN)
- 建立大根堆的过程
- 时间复杂度:O(N)==> log1 + log2 + log3 …
heapify
堆中有一个值(X)发生变化了,怎么调整才能让这个堆仍然是大根堆
- 找到X的左节点和右节点,较大的值(M)和X比较
- 如果X < M:X和M交换
示例
有一个流,不断在吐出数(无序),随时找到流中的中位数
可行方案
- 方案一
- 用一个数组装这些数(O(1))
- 每次需要中位数的时候,对数组排序(O(NlogN))
- 取出中位数
- 方案二
- 使用两个数组(大根堆、小根堆),较小的N/2个都放在大根堆中,较大的N/2都放在小根堆中
- 当两个数组的长度大于1的时候,从多的数组中,减堆(
弹出大根堆/小根堆的堆顶
),放到少的数组中- 先把第一个和最后一个元素交换(最大值和最小值交换)
- 然后将heap_size减1(从堆中去掉最大值)
- 然后经历一遍heapify的过程,继续保持大根堆、小根堆
- 大根堆和小根堆的堆顶,就是两个数组中的最大值和最小值
- 一直保持,较小的N/2个在大根堆中,较大的N/2个在小根堆中
- 这样就可以随时拿到中位数
代码实现
堆排序
时间复杂度O(N*logN)
额外空间复杂度O(1)
- 把数组通过heap_insert变成大根堆
- 把最后一个值和堆顶交换
- 把堆的大小减1 ==> 最大值保持不变的放在了最后一个位置,已经不在堆中了
- 再从剩下的堆中,做heapify的调整(堆顶被交换成了最小值),又变成了大根堆
- 再把当前堆中的最后一个值,和堆顶交换
- … …
class HeapSort:
def sort(self, arr: list):
"""启动堆排序"""
if arr is None or len(arr) < 2:
return
# 0~i 上形成大根堆
for i in range(len(arr)):
self.heap_insert(arr, i)
# 最后一个数和第一个数交换
heap_size = len(arr) - 1
arr[0], arr[heap_size] = arr[heap_size], arr[0]
# 直到大根堆的长度为0,所有的数就都有序了
while heap_size > 0:
self.heapify(arr, 0, heap_size)
arr[0], arr[heap_size - 1] = arr[heap_size - 1], arr[0]
# 进过上述操作之后,把heap_size - 1
heap_size -= 1
return arr
def heap_insert(self, arr: list, index: int):
"""在大根堆的基础上添加一个元素,heapInsert使其依然是大根堆"""
# 这个while是为了能 找到所有的父节点
while arr[index] > arr[int((index - 1) / 2)]:
# 只要我比我当前父节点的值大,我就和它完成交换
arr[index], arr[int((index - 1) / 2)] = arr[int((index - 1) / 2)], arr[index]
# 同时index向上走
# 当i走到0的时候,会出现arr[0] = arr[(0-1)/2], while也会停止
index = int((index - 1) / 2)
def heapify(self, arr: list, index: int, heap_size: int):
"""heap_size: 已经形成的堆的大小"""
# 当前元素的左节点
left = index * 2 + 1
# 这个while是为了能 找到所有的子节点
while left < heap_size:
# 找到左右节点中的 较大值(largest)
if left + 1 < heap_size and arr[left + 1] > arr[left]:
# 右节点也不越界,且 右节点>左节点
largest = left + 1
else:
largest = left
# 我和左右两个节点比较,如果还是我最大,就不用交换
largest = largest if arr[largest] > arr[index] else index
if largest == index:
break
arr[largest], arr[index] = arr[index], arr[largest]
index = largest
left = index * 2 + 1
list_ = [2, 4, 3, 6, 5, 8, 7, 10, 9]
arr = HeapSort().sort(list_)
print(arr)
评论区