(2020.12.21)
堆: 用树形结构实现的优先队列,完全二叉树。
任意节点保存的数据,在优先级上优于或等于其子节点的数据。从根到任意叶的路径,优先级(非严格)递减。堆中最优先数据在根节点,检索复杂度O(1)。
插入元素:向堆(完全二叉树)的结尾加入一个元素,此时二叉树不是堆。新加入元素和父节点做对比,如果新的优先级高就与父节点交换,并重复该过程;否则结束插入元素的过程。复杂度O(log2(n)).该过程称为向上筛选。
弹出元素:弹出优先级最高的元素,即堆顶的元素。弹出后堆顶空缺,从堆的尾部取出最后一个元素放在堆顶,并比较堆顶和两个子节点,将优先级最高的放在顶点,此时原来处在堆尾的点就下移,重复这个过程直到堆形成。称为向下筛选。复杂度O(log2(n)).
(2022.05.03 Tue)
实现
本实现案例使用Python的list保存heap数据。用list元素的值作为优先级,将最小的元素放在堆顶,以此类推。
先实现HeapBase基类,其中定义了若干基本动作,包括
- 判断有无左/右子节点
- 找出左/右子节点
- 子父节点交换
- 从heap尾部插入数据并upheap到符合heap标准的位置
- 调整非尾部数据到符合heap标准的位置
heap类继承了HeapBase基类,还加入了若干动作,包括
- 向heap加入新元素
- 找出heap的最小值
- 移除heap的最小值
class HeapBase:
def __init__(self, data=[]):
self._data = data
def __len__(self):
return len(self._data)
def min(self):
return self._data[0]
def _parent(self, j):
return (j-1)//2
def _left(self, j):
left = j*2 + 1
return left
def _right(self, j):
right = j*2 + 2
return right
def _has_left(self, j):
return len(self._data) > self._left(j)
def _has_right(self, j):
return len(self._data) > self._right(j)
def _swap(self, i, j):
self._data[i], self._data[j] = self._data[j], self._data[i]
def _upheap(self, j):
parent = self._parent(j)
if parent >= 0 and self._data[j] < self._data[parent]:
self._swap(parent, j)
self._upheap(parent)
else:
pass
def _downheap(self, j):
if self._has_left(j):
left = self._left(j)
smaller = left
if self._has_right(j):
right = self._right(j)
if self._data[right] < self._data[left]:
smaller = right
if self._data[smaller] < self._data[j]:
self._swap(smaller, j)
self._downheap(smaller)
class heap(HeapBase):
def __init__(self, data=[]):
self._data = data
def __len__(self):
return len(self._data)
def add(self, nod):
self._data.append(nod)
if len(self._data) == 1:
pass
else:
super()._upheap(len(self._data)-1)
def min(self):
if len(self._data) == 0:
raise 'Empty heap'
return self._data[0]
def remove_min(self):
if len(self._data) == 0:
raise 'Empty heap'
super()._swap(0, len(self._data)-1)
res = self._data.pop()
super()._downheap(0)
return res
下面将一个list推入heap并排序输出
>>> a = heap()
>>> tmp = [9, 85, 96, 93, 37, 81, 30, 50, 31, 54]
>>> for i in tmp:
... a.add(i)
...
>>> for i in range(len(tmp)):
... a.remove_min()
...
9
30
31
37
50
54
81
85
93
96
复杂度分析
- 添加新元素:和list的append复杂度相同,amortised
O(1)
,以及upheap的复杂度O(logn)
,总复杂度O(logn)
- 找到最小元素:
O(1)
- 去除最小元素:交换
O(1)
,删除O(1)
,downheapO(logn)
,总复杂度O(logn)
应用 - 排序
对长度为n的list排序,先将其添加到heap中,在以此去除最小元素,总计算量是n*O(logn)+n*O(logn)
,总复杂度O(nlogn)
。
Reference
1 Goodrich and etc., Data Structures and Algorithms in Python