注意到,用链表表示列表的时候,不需要考虑队列满的情况
def dequeue(self):
if self.is_empty():
raise Exception('Queue is empty')
else:
node = self.front.next
self.front.next = node.next
if self.rear == node:
self.rear = self.front
value = node.value
del node
return value
事实上这句代码中,self.front并没有发生改变,一直指的是head,只是将self.front.next移动
接下来是优先队列的堆实现,不得不学习一下堆。
def push(self, val: int):
# 将新元素添加到堆的末尾
self.max_heap.append(val)
size = len(self.max_heap)
# 从新插入的元素节点开始,进行上移调整
self.__shift_up(size - 1)
def pop(self) -> int:
# 堆为空
if not self.max_heap:
raise IndexError("堆为空")
size = len(self.max_heap)
self.max_heap[0], self.max_heap[size - 1] = self.max_heap[size - 1], self.max_heap[0]
# 删除堆顶元素
val = self.max_heap.pop()
# 节点数减 1
size -= 1
self.__shift_down(0, size)
def __buildMaxHeap(self, nums: [int]):
size = len(nums)
# 先将数组 nums 的元素按顺序添加到 max_heap 中
for i in range(size):
self.max_heap.append(nums[i])
# 从最后一个非叶子节点开始,进行下移调整
for i in range((size - 2) // 2, -1, -1):
self.__shift_down(i, size)
def maxHeapSort(self, nums: [int]) -> [int]:
# 根据数组 nums 建立初始堆
self.__buildMaxHeap(nums)
size = len(self.max_heap)
for i in range(size - 1, -1, -1):
# 交换根节点与当前堆的最后一个节点
self.max_heap[0], self.max_heap[i] = self.max_heap[i], self.max_heap[0]
# 从根节点开始,对当前堆进行下移调整
self.__shift_down(0, i)
针对于大顶堆,插入(push)就是插到末尾然后往上走,此时size-1是因为它是最后一个,要和父节点比较;删除(指的是从堆中移除位于堆顶的元素pop)就是和堆顶交换然后pop然后堆顶往下移并且size-1确保大顶堆的性质。交换元素类似于删除但没有pop但也有size-1。其实size-1也有不同的含义了。对于交换堆的长度其实没有变化。
-
构建初始大顶堆:从数组的中间位置开始,从右至左,依次通过「下移调整」将数组转换为一个大顶堆。(为什么?因为大顶堆是个完全二叉树,确保所有子树都满足大顶堆的性质。)
# 从最后一个非叶子节点开始,进行下移调整 for i in range((size - 2) // 2, -1, -1): self.__shift_down(i, size)
从中间开始,就是(size-2)//2
-
接下来学习优先队列。大多是其实差不多。
class Heapq: # 堆调整方法:调整为大顶堆 def heapAdjust(self, nums: [int], index: int, end: int): left = index * 2 + 1 right = left + 1 while left <= end: # 当前节点为非叶子结点 max_index = index if nums[left] > nums[max_index]: max_index = left if right <= end and nums[right] > nums[max_index]: max_index = right if index == max_index: # 如果不用交换,则说明已经交换结束 break nums[index], nums[max_index] = nums[max_index], nums[index] # 继续调整子树 index = max_index left = index * 2 + 1 right = left + 1
这里的headAdjust就是堆里面下移(shift down)
-
最后总结一下啊。headify,headpop,headSort都要用到headAdjust来下移元素保持大顶栈。
-
import heapq class PriorityQueue: def __init__(self): self.queue = [] self.index = 0 def push(self, item, priority): heapq.heappush(self.queue, (-priority, self.index, item)) self.index += 1 def pop(self): return heapq.heappop(self.queue)[-1]
heappush中-priority是负的,因为下面heappop(self.queue)返回一个元组,优先级最低(因为它是顶大堆)的元素,但是我们用了负数,反而弹出的原本优先级最高的了。