堆是一种数据结构,因为Heapsort而被提出。除了堆排序,“堆”这种数据结构还可以用于优先队列的实现。
堆首先是一个完全二叉树:它除了最底层之外,树的每一层的都是满的,且最底层中的节点处于左边,相互之间没有“跳变”;其次,堆有次序属性:每个节点中的数据项都大于或者等于其子女的数据项(如果是记录,则这些记录中的某个关键域必须满足这一属性)。 当然,这是指大顶堆,小顶堆则是父节点比子节点都要小。
所谓队列,就是一个FIFO表(first in, first out)。优先队列,就是在队列的基础上,每个元素加一个优先级,last in的元素可能会first out,这就是优先级在起作用。
我想实现这样一个优先队列:
元素为整数
元素值小的优先级反而大
有入队操作,每次进入一个元素
出队操作,也行每次一个元素
那么,我的小根堆Heap应该这样考虑:
每当插入元素时,在原有Heap的最后一个叶子节点后面插入新元素val,并持续比较val和其父节点的值之间是否满足堆的次序属性,直到满足
每当删除元素时,删除的是值最小的元素,也就是根结点root,则将root和最后一个叶子节点lastleaf互换,然后删除交换后的new_lastleaf ,并从new_root开始调整堆,使满足堆的次序属性。
这样一来,代码就不难写出:
#coding:utf8 #author:HaxtraZ #description:优先队列,用堆实现 #修改自《算法导论》2nd Edition class ZHeap: def __init__(self, item=[]): # 初始化。item为数组 self.items = item self.heapsize = len(self.items) def LEFT(self, i): return 2 * i + 1 def RIGHT(self, i): return 2 * i + 2 def PARENT(self, i): return (i - 1) / 2 def MIN_HEAPIFY(self, i): # 最小堆化:使以i为根的子树成为最小堆 l = self.LEFT(i) r = self.RIGHT(i) if l < self.heapsize and self.items[l] < self.items[i]: smallest = l else: smallest = i if r < self.heapsize and self.items[r] < self.items[smallest]: smallest = r if smallest != i: self.items[i], self.items[smallest] = self.items[smallest], self.items[i] self.MIN_HEAPIFY(smallest) def INSERT(self, val): # 插入一个值val,并且调整使满足堆结构 self.items.append(val) idx = len(self.items) - 1 parIdx = self.PARENT(idx) while parIdx >= 0: if self.items[parIdx] > self.items[idx]: self.items[parIdx], self.items[idx] = self.items[idx], self.items[parIdx] idx = parIdx parIdx = self.PARENT(parIdx) else: break self.heapsize += 1 def DELETE(self): last = len(self.items) - 1 if last < 0: # 堆为空 return None # else: self.items[0], self.items[last] = self.items[last], self.items[0] val = self.items.pop() self.heapsize -= 1 self.MIN_HEAPIFY(0) return val def BUILD_MIN_HEAP(self): # 建立最小堆, O(nlog(n)) i = self.PARENT(len(self.items) - 1) while i >= 0: self.MIN_HEAPIFY(i) i -= 1 def SHOW(self): print self.items class ZPriorityQ(ZHeap): def __init__(self, item=[]): ZHeap.__init__(self, item) def enQ(self, val): ZHeap.INSERT(self, val) def deQ(self): val = ZHeap.DELETE(self) return val a = [1, 3, 2, 4, 8, 6, 22, 9] pq = ZPriorityQ() n = len(a) for i in range(n): pq.enQ(a[i]) pq.SHOW() for i in range(n): pq.deQ() pq.SHOW()
其中,ZHeap表示小根堆,ZPriorityQ表示优先队列,deQ表示退队,enQ表示入队。
我们发现以下结论:大根堆用于升序排序,小根堆用于降序排序。
为什么用堆来实现优先队列?原因只有一个:复杂度低。
如果使用列表(存放在list中),插入为O(1),删除为O(n);
如果使用按照优先级排好序的有序列表,插入和线性插入排序一样,O(n),删除则为O(1)
使用堆的时候,无论你删除还是插入,都是O(lg n)时间复杂度,对于插入和删除都比较频繁的操作来讲,这是最好不过的了。
如果认为有不正确的地方欢迎拍砖。