10.6 优先队列
优先队列
(priority queue)可以在 时间内获得最大值,并且可以在 O(log n) 时间内取出最大值或插入任意值。
优先队列常常用堆(heap)来实现。堆是一个完全二叉树,其每个节点的值总是大于等于子节点的值。实际实现堆时,我们通常用一个数组而不是用指针建立一个树。这是因为堆是完全二叉树,所以用数组表示时,位置 i 的节点的父节点位置一定为 (i-1)/2,而它的两个子节点的位置又一定分别为 2i+1 和 2i+2。
以下是堆的实现方法,其中最核心的两个操作是上浮和下沉:如果一个节点比父节点大,那么需要交换这个两个节点;交换后还可能比它新的父节点大,因此需要不断地进行比较和交换操作,我们称之为上浮;类似地,如果一个节点比父节小,也需要不断地向下进行比较和交换操作我们称之为下沉。如果一个节点有两个子节点,我们总是交换最大的子节点。
- C++
- Python
class Heap {
public:
Heap() {}
// 上浮。
void swim(int pos) {
int next_pos = (pos - 1) / 2;
while (pos > 0 && heap_[next_pos] < heap_[pos]) {
swap(heap_[next_pos], heap_[pos]);
pos = next_pos;
next_pos = (pos - 1) / 2;
}
}
// 下沉。
void sink(int pos) {
int n = heap_.size();
int next_pos = 2 * pos + 1;
while (next_pos < n) {
if (next_pos < n - 1 && heap_[next_pos] < heap_[next_pos + 1]) {
++next_pos;
}
if (heap_[pos] >= heap_[next_pos]) {
break;
}
swap(heap_[next_pos], heap_[pos]);
pos = next_pos;
next_pos = 2 * pos + 1;
}
}
// 插入任意值:把新的数字放在最后一位,然后上浮。
void push(int k) {
heap_.push_back(k);
swim(heap_.size() - 1);
}
// 删除最大值:把最后一个数字挪到开头,然后下沉。
void pop() {
heap_[0] = heap_.back();
heap_.pop_back();
sink(0);
}
// 获得最大值。
int top() { return heap_[0]; }
private:
vector<int> heap_;
};
class Heap:
def __init__(self):
self.heap = []
# 上浮。
def swim(self, pos: int):
next_pos = (pos - 1) // 2
while pos > 0 and self.heap[next_pos] < self.heap[pos]:
self.heap[next_pos], self.heap[pos] = self.heap[pos], self.heap[next_pos]
pos = next_pos
next_pos = (pos - 1) // 2
# 下沉。
def sink(self, pos: int):
n = len(self.heap)
next_pos = 2 * pos + 1
while next_pos < n:
if next_pos < n - 1 and self.heap[next_pos] < self.heap[next_pos + 1]:
next_pos += 1
if self.heap[pos] >= self.heap[next_pos]:
break
self.heap[next_pos], self.heap[pos] = self.heap[pos], self.heap[next_pos]
pos = next_pos
next_pos = 2 * pos + 1
# 插入任意值:把新的数字放在最后一位,然后上浮。
def push(self, k: int):
self.heap.append(k)
self.swim(len(self.heap) - 1)
# 删除最大值:把最后一个数字挪到开头,然后下沉。
def pop(self):
self.heap[0] = self.heap.pop()
self.sink(0)
# 获得最大值。
def top(self) -> int:
return self.heap[0]
通过将算法中的大于号和小于号互换,我们也可以得到一个快速获得最小值的优先队列。