二叉堆(Binary Heap)
- 二叉堆概述
- 优先级队列
二叉堆(Binary Heap)其实比较简单,但却非常有用,常见的应用二叉堆排序和优先级队列。本文将介绍二叉堆的基本性质、基本操作和二叉堆在优先级队列上的应用。
二叉堆概述
二叉堆其实就是一个完全二叉树(完全二叉树的介绍见二叉树(Binary Tree):先序遍历、中序遍历、后序遍历和层次遍历),只不过用数组存储形式。
这里特意保留arr[0]不用,这样可以方便地计算父亲和儿子节点的index。因此,arr[1]是二叉树的根节点。二叉堆分为最大堆和最小堆两种:
- 最大堆:每个节点的值都大于等于它的两个子节点。
- 最小堆:每个节点的值都小于等于它的两个子节点。
显然,最大堆最大的元素就是其堆顶arr[1],最小堆最小的元素就是其堆顶arr[1]。
优先级队列
优先级队列(Priority Queue)在插入或者删除元素后,会自动完成顺序更新,这种顺序的更新是使用了二叉堆来完成的。优先级队列最重要的操作就是插入和删除(使用最大堆就是delMax,使用最小堆就是delMin),主要接口如下:
template <typename T> // 元素类型
class MaxPriQueue {
private:
T* pq
int num = 0;
public:
MaxPriQueue(int n) {
pq = new T[n + 1];
}
void insert(T value);
T delMax()
private:
int parent(int root) {
return root / 2;
}
int left(int root) {
return root * 2;
}
int right(int root) {
return root * 2 + 1;
}
T max() {
return pq[1];
}
void swap(int i, int j) {
T tmp = pq[i];
pq[i] = pq[j];
pq[j] = tmp;
}
bool less(int i, int j) {
return pq[i] < pq[j];
}
void swim(int x); // 上浮第x个元素
void sink(int x); // 下沉第x个元素
}
二叉堆最重要的两个操作就是上浮(swim)和下沉(sink),二叉堆就是靠这两个方法维护其基本性质。这里都以以最大堆为例,最小堆情况是类似的。当我们在二叉堆里插入或者删除元素时候,肯定会破坏二叉堆的性质。当某个父节点元素小于其子节点元素时,就需要将其与子节点进行交换,也就是下沉。反之,就需要上浮。
节点的上浮可能需要多次才能到达正确的位置,代码如下:
void swim(int x) {
while(x > 1 && less(parent(x), x)) {
sawp(parent(x), x);
x = parent(x);
}
}
节点A下沉需要比较两个子节点,只要比其中之一小就需要下沉,并且将较大的子节点与A节点交换。代码如下:
void sink(int x) {
while(left(x) <= num) {
int maxIndex = left(x);
if(right(x) <= num && less(maxIndex, right(x))) {
maxIndex = right(x);
}
if(less(maxIndex, x)) break;
swap(x, maxIndex);
x = maxIndex;
}
}
有了上浮和下沉两个方法,优先级队列的insert和delMax就比较容易了。insert就是将元素插入数组尾部,然后再执行上浮到正确位置即可。
void insert(T value) {
num++;
pq[num] = value;
swim(num);
}
delMax只需要将堆顶元素A和堆底元素B交换,然后删除堆底的A,再将堆顶的B下沉到正确位置即可。
T delMax() {
T max = pq[1];
swap(1, num);
pq[num] = nullptr;
num--;
sink(1);
return max;
}
二叉堆就是一棵完全二叉树,非常适合使用数组存储,而二叉堆的性质主要就是靠上浮和下沉来维护。
C++标准库有优先级队列可直接使用:
template<
class T,
class Container = std::vector<T>,
class Compare = std::less<typename Container::value_type>
> class priority_queue;
详情戳这里。优先级队列的应用可以刷一下力扣合并 K 个升序链表。
参考:
- https://labuladong.github.io/algo/di-yi-zhan-da78c/shou-ba-sh-daeca/er-cha-dui-1a386/
- https://en.cppreference.com/w/cpp/container/priority_queue