一、完全二叉树
- 若二叉树的深度为h,则除第h层外,其他层的结点全部达到最大值,且第h层的所有结点都集中在左子树。
二、定义
- 堆是一颗完全二叉树;
- 堆中的某个结点的值总是大于等于(最大堆)或小于等于(最小堆)其孩子结点的值。
- 堆中每个结点的子树都是堆树。
三、初始化
因为堆是二叉树,因此可以用顺序表存储。
class PrioQueue:
def __index__(self, elist=[]):
self._elist = list(elist)
if elist:
self.buildheap()
def is_empty(self):
return len(self.is_empty()==0)
def peek(self):
if self.is_empty():
raise PrioQueueError("in peek")
return self._elist[0]
四、插入
插入元素和向上筛选
- 把新加入的元素放在(连续表)已有元素之后,执行一次向上筛选操作。
- O(logn)
def enqueue(self, e):
self._elist.append(None)
self.siftup(e, len(self._elist)-1)
def siftup(self, e, last):
elems, i, j = self._elist, last, (last-1)//2
while i > 0 and e < elems[j]:
elems[i] = elems[j]
i, j = j, (j-1)//2
elems[i] = e
五、删除
弹出元素和向下筛选
- 弹出当时的堆顶。
- 从堆最后取一个元素作为完全二叉树的根。
- 执行一次向下筛选。
def dequeue(self):
if self.is_empty():
raise PrioQueueError("in dequeue")
elems = self._elist
e0 = elems[0]
e = elems.pop()
if len(elems) > 0:
self.siftdown(e, 0, len(elems))
return e0
def siftdown(self, e, begin, end):
elems, i ,j = self._elist, begin, begin * 2 + 1
while j < end:
if j + 1 < end and elems[j+1] < elems[j]:
j += 1
if e < elems[j]:
break
elems[i] = elems[j]
i, j = j, 2 * j + 1
elems[i] = e
六、构建堆
- O(n)
def buildheap(self):
end = len(self._elist)
for i in range(end//2, -1, -1):
self.siftdown(self._elist[i], i, end)
七、堆排序
- 首先构建小顶堆
- 每次弹出的就是最小值,希望能不利用其他空间,每次弹出就后面就会空出来一个位置,正好存放弹出的元素,不过这样的到的结果是从大到小,可以反转,或者直接构建大顶堆。
def heap_sort(elems):
def siftdown(elems, e, begin, end):
i ,j = begin, begin * 2 + 1
while j < end:
if j + 1 < end and elems[j+1] < elems[j]:
j += 1
if e < elems[j]:
break
elems[i] = elems[j]
i, j = j, 2 * j + 1
elems[i] = e
end = len(elems)
# buid heap O(n)
for i in range(end//2, -1, -1):
siftdown(elems, elems[i], i, end)
# O(nlogn)
for i in range((end - 1), 0, -1):
e = elems[i]
elems[i] = elems[0]
siftdown(elems, e, 0, i)