优先队列的基本操作:
实现了这三个需求的抽象数据类型就叫优先队列
其实大家都知道优先队列一般是用“堆”实现的,但原则上这不是必须的,下面看看用各种方法实现优先队列时的工作效率:
实现方法 | 插入 | 删除最小 | 查找最小 |
---|---|---|---|
无序数组 | 1 | n | n |
无序链表 | 1 | n | n |
有序数组 | n | 1 | 1 |
有序链表 | n | 1 | 1 |
二叉搜索树 | logn | logn | logn |
平衡二叉搜索树 | logn | logn | logn |
二叉堆 | logn | logn | 1 |
现在你知道为什么要用堆来实现优先队列了吧
从结构来看,堆是完全二叉树
从节点来看,堆中的父节点的值必须不大于(不小于)其子节点的值
一般也叫做最小堆(最大堆)
以下是最小堆的实现
class Heap:
def __init__(self, l=[]):
self.heapList = [None]+l # Elements in Heap
self.buildHeap()
@property
def size(self):
return len(self.heapList) - 1
def parent(self, index):
return index // 2
def leftChildIndex(self, index):
return 2 * index
def rightChildIndex(self, index):
return 2 * index + 1
def validIndex(self, index):
return index <= self.size and index >= 1
def hasChild(self, index):
return self.validIndex(index*2)
def leftChild(self, index):
lindex = self.leftChildIndex(index)
return self.heapList[lindex] if self.validIndex(lindex) else None
def rightChild(self, index):
rindex = self.rightChildIndex(index)
return self.heapList[rindex] if self.validIndex(rindex) else None
def minimumChild(self, i):
if not self.hasChild(i):
return None
elif not self.validIndex(self.rightChildIndex(i)):
return self.leftChildIndex(i)
lindex = self.leftChildIndex(i)
rindex = self.rightChildIndex(i)
return lindex if self.heapList[lindex] < self.heapList[rindex] else rindex
def __repr__(self):
return str(self.heapList[1:])
首先从上面的代码可以发现在完全二叉树中索引父节点和子节点非常容易!
下面来看一些常用操作:
这个对于堆来说没有优势,只能遍历
def searchElement(self, itm):
i = 1
while (self.validIndex(i)):
if itm == self.heapList[i]:
return i
i += 1
return None
直接返回根节点,需要 O ( log ( 1 ) ) O(\log(1)) O(log(1))时间
def getMinimum(self):
return None if self.size == 0 else self.heapList[1]
使用另一个堆来保存临时结果,时间复杂度为 O ( K log K ) O(K\log K) O(KlogK)
def topK(self, k):
assert(k >= 0 and k <= self.size)
result = []
HAux = Heap()
item = self.getMinimum()
HAux.insert(item)
while (len(result) < k):
item = HAux.deleteMin()
result.append(item)
if self.rightChild(self.searchElement(item)) is not None:
HAux.insert(self.rightChild(self.searchElement(item)))
if self.leftChild(self.searchElement(item)) is not None:
HAux.insert(self.leftChild(self.searchElement(item)))
return result
从最小堆里找最大值,只要遍历叶子节点即可, O ( n 2 ) = O ( n ) O(\frac{n}{2})=O(n) O(2n)=O(n)
def getMaximum(self):
maximum = self.heapList[self.parent(self.size)]
for i in range(self.parent(self.size)+1, self.size+1):
if self.heapList[i] > maximum:
maximum = self.heapList[i]
return maximum
删除根节点后需要从上之下调整一遍:percolateDown,需要 O ( log ( n ) ) O(\log(n)) O(log(n))时间
def swap(self, a, b):
self.heapList[a], self.heapList[b] = self.heapList[b], self.heapList[a]
def percolateDown(self, i):
while self.hasChild(i):
minimumChild = self.minimumChild(i)
if self.heapList[i] > self.heapList[minimumChild]:
self.swap(i, minimumChild)
i = minimumChild
def deleteMin(self):
retval = self.heapList[1]
self.swap(1, self.size)
self.heapList.pop()
self.percolateDown(1)
return retval
和删除最小值相比,删除其他元素的前提是先从堆中找到该元素,这需要 O ( n ) O(n) O(n) 时间,然后将该元素和末端的叶子节点交换,删除末端节点,然后从被删除节点的原始位置开始调整堆。
def delete(self, key):
loc = self.searchElement(key)
if loc is not None:
self.swap(loc, self.size)
self.heapList.pop()
self.percolateDown(loc)
一般直接插入末尾,然后从下至上调整堆 percolateUp,需要 O ( l o g n ) O(logn) O(logn)时间
def percolateUp(self, i):
while self.validIndex(i // 2):
if self.heapList[i] < self.heapList[i // 2]:
self.swap(i, i//2)
i = i // 2
def insert(self, k):
self.heapList.append(k)
self.percolateUp(self.size)
从数组构造堆,图示可见《堆排序建立初始堆》
def buildHeap(self):
n = self.size
for i in range(self.parent(n), 0, -1):
self.heapify(i)
def heapify(self, index):
left = self.leftChildIndex(index)
right = self.rightChildIndex(index)
minimum = index
if self.validIndex(left) and self.heapList[left] < self.heapList[index]:
minimum = left
if self.validIndex(right) and self.heapList[right] < self.heapList[minimum]:
minimum = right
if minimum != index:
self.swap(minimum, index)
self.heapify(minimum)
def heapSort(self):
result = []
while self.size > 0:
result.append(self.deleteMin())
return result
证明: