📜  使用二叉堆的优先队列(1)

📅  最后修改于: 2023-12-03 15:36:37.935000             🧑  作者: Mango

使用二叉堆的优先队列

简介

优先队列是一个常见的数据结构,它可以将元素按照优先级进行排序,并支持在队列的任意位置插入和删除元素。二叉堆是一种常用的实现优先队列的数据结构,它的时间复杂度对于大多数常见操作都是 O(log n)。

实现

二叉堆是一种完全二叉树(即除了最后一层外,其它层的节点都是满的,最后一层的节点依次从左到右排列),它分为大根堆和小根堆两种。大根堆的每个节点都要大于等于它的子节点,小根堆则每个节点都要小于等于它的子节点。

我们可以用数组来表示二叉堆,其中数组下标与二叉树的节点一一对应。具体来说,对于下标为 i 的节点,它的左子节点下标为 2i,右子节点下标为 2i+1,它的父节点下标为 i/2(整数除法)。

下面是一个二叉堆的示例,其中橙色节点为根节点,红色为左右子节点。

Binary Heap

操作
插入元素

插入元素的操作很简单,我们将元素添加到二叉堆的末尾,然后将它与它的父节点比较,如果它比父节点大(或小,根据要实现的是大根堆还是小根堆而定),则交换它们的位置,这样每次操作都能保证二叉堆的性质。

def insert(heap, val):
    heap.append(val)
    i = len(heap) - 1
    while i > 1 and heap[i//2] < heap[i]:
        heap[i], heap[i//2] = heap[i//2], heap[i]
        i //= 2

# example usage
heap = []
insert(heap, 3)
insert(heap, 1)
insert(heap, 4)
insert(heap, 1)
insert(heap, 5)
print(heap)  # Output: [5, 3, 4, 1, 1]
删除堆顶元素

删除堆顶元素的操作也很简单,我们将堆顶元素(即数组的第一个元素)与最后一个元素交换位置,然后将它与它的子节点比较,如果它比子节点小(或大,根据要实现的是大根堆还是小根堆而定),则交换它们的位置,这样也能保证二叉堆的性质。最后再将堆顶元素移除即可。

def delete_top(heap):
    if len(heap) == 1:
        return heap.pop()
    top = heap[1]
    heap[1] = heap.pop()
    i = 1
    while True:
        left = i * 2 if i * 2 < len(heap) else None
        right = i * 2 + 1 if i * 2 + 1 < len(heap) else None
        if left is None and right is None:
            break
        elif left is None:
            max_child = right
        elif right is None:
            max_child = left
        else:
            max_child = left if heap[left] > heap[right] else right
        if heap[i] < heap[max_child]:
            heap[i], heap[max_child] = heap[max_child], heap[i]
            i = max_child
        else:
            break
    return top

# example usage
heap = []
insert(heap, 3)
insert(heap, 1)
insert(heap, 4)
insert(heap, 1)
insert(heap, 5)
print(delete_top(heap))  # Output: 5
print(heap)  # Output: [4, 3, 1, 1]
获取堆顶元素

获取堆顶元素的操作也很简单,只需要返回数组的第一个元素即可。

def get_top(heap):
    if len(heap) > 1:
        return heap[1]
    return None

# example usage
heap = []
insert(heap, 3)
insert(heap, 1)
insert(heap, 4)
insert(heap, 1)
insert(heap, 5)
print(get_top(heap))  # Output: 5
性能

二叉堆的时间复杂度如下:

  • 插入元素:O(log n)
  • 删除堆顶元素:O(log n)
  • 获取堆顶元素:O(1)

由于二叉堆是一种完全二叉树,其空间复杂度是 O(n)。和其他一些实现优先队列的数据结构相比,它通常具有更好的常数因子,所以在实践中常常使用二叉堆来实现优先队列。