二叉堆
堆是一棵具有特定性质的二叉树-完全二叉树。堆的基本要求是堆中所有结点的值必须大于或等于(或小于或等于)其孩子结点的值。除此以外,所有叶子结点都是处于第 h 或 h - 1层(h为树的高度),其实堆也是一个完全二叉树。堆分为最大堆和最小堆。
使用数组存储 下标从1开始
- 父节点 = i / 2
- 左节点 2 * i
- 右节点 2 * i + 1
使用数组存储 下标从0开始
- 父节点 = (i-1) / 2
- 左节点 2 * i + 1
-
最大堆实现
class MaxHeap:"""最大堆"""def __init__(self, capacity=20):self.data = []self.count = 0self.capacity = capacity# 获取堆的元素个数def size(self):return self.count# 是否为空def is_empty(self):return self.count == 0# 返回堆的数组表示中 一个索引所表示的元素的父节点的索引@staticmethoddef parent(index: int) -> int:assert index >= 0, " index 0 无父节点"return (index - 1) // 2# 返回索引所表示元素的左孩子节点索引@staticmethoddef left_child(index: int) -> int:return index * 2 + 1# 返回索引所表示元素的右孩子节点索引@staticmethoddef right_child(index: int) -> int:return index * 2 + 2# 向堆中添加元素def insert(self, data: int):assert self.count + 1 < self.capacity, "capacity error"self.data.append(data)self.count += 1self.shift_up(self.count-1)# 位置交换def shift_up(self, index: int):# 找到父元素的值和当前值进行比较# 大于父元素进行数据交换while self.data[self.parent(index)] < self.data[index] and index > 0:self.__swap(self.parent(index), index)index = self.parent(index)# 数据交换def __swap(self, i: int, j: int):assert 0 <= i <= self.size() - 1 and 0 <= j <= self.size() - 1, "i j error "self.data[i], self.data[j] = self.data[j], self.data[i]# 取出根节点(最大值)def extract_max(self) -> int:assert 0 < self.count, "max_heap is empty "# 需要去除的数据delete_data = self.data[0]# 最后一个元素替换到跟self.__swap(0, self.count - 1)self.count -= 1# 删除最后一个数据self.data.pop()self.shift_down(0)return delete_data# 从上往下矫正最大堆def shift_down(self, index: int):# 确保有一个孩子节点while self.left_child(index) < self.count:j = self.left_child(index) # 左节点的位置# 右孩子大于左孩子if (j + 1) <= self.count - 1 and self.data[j + 1] > self.data[j]:# 否则左孩子大于右孩子j += 1# 当前节点和孩子节点最大值做判断if self.data[index] >= self.data[j]:breakself.__swap(index, j)index = jdef show_data(self):print(self.data)
最大堆排序
class MaxHeapSort:# 最大堆排序def __init__(self):self.max_heap = MaxHeap()def sort(self,data:list):[self.max_heap.insert(i) for i in data]for i in range(len(data))[::-1]:data[i] = self.max_heap.extract_max()
测试
data = [5,6,7,8,9,1,0]
max_heap_sort = MaxHeapSort()
max_heap_sort.sort(data)
print(data)
[0, 1, 5, 6, 7, 8, 9]
replace
取出最大元素后,放入一个新的元素
方案1:先取出最大值 再添加一个新值,期间需要两次O(logn)操作
方案2:堆顶元素替换后直接siftdown 执行一次操作 期间执行一次O(logn)操作
def replace(self,v:int):max_v = self.data[0]self.data[0] = vself.shift_down(0)return max_v
heapify
对任意数组组装成最大堆结构
# 将任意数据组装成堆的结构def heapify(self):# 从最后一个非叶子节点开始 shift_downfor i in range(self.parent(len(self.data)-1))[::-1]:self.shift_down(i)
优化最大堆排序 - 就地排序
class MaxHeapSort:# 最大堆就地排序def sort(self, data: list):# 就地排序if len(data) <= 1:return# 从最后一个非叶子节点开始 shift_down# 先构造成一个最大堆for i in range(self.parent(len(data) - 1))[::-1]:self.shift_down(data, i, len(data))# 开始从堆顶替换元素到末尾# 然后依次替换并重新整理为堆for j in range(len(data))[::-1]:data[0], data[j] = data[j], data[0]self.shift_down(data, 0, j)# 从上往下矫正最大堆def shift_down(self, data, index: int, data_len):# 确保有一个孩子节点while self.left_child(index) < data_len:j = self.left_child(index) # 左节点的位置# 右孩子大于左孩子if (j + 1) < data_len and data[j + 1] > data[j]:# 否则左孩子大于右孩子j += 1# 当前节点和孩子节点最大值做判断if data[index] >= data[j]:breakdata[index], data[j] = data[j], data[index]index = j# 返回索引所表示元素的左孩子节点索引@staticmethoddef left_child(index: int) -> int:return index * 2 + 1# 返回索引所表示元素的右孩子节点索引@staticmethoddef right_child(index: int) -> int:return index * 2 + 2# 返回堆的数组表示中 一个索引所表示的元素的父节点的索引@staticmethoddef parent(index: int) -> int:assert index >= 0, " index 0 无父节点"return (index - 1) // 2
