二叉堆基础与优先队列

堆是一种数据结构,而priority_queue (优先队列)是一个基于二叉堆的STL容器。

堆有很多种类,但以下提及的堆均为二叉堆。

以下是一个例子。将新元素20插入二叉堆:




所以对一个二叉堆,有3种基本操作:

  1. 插入:将 val 插入堆。

  2. 取堆顶:查看堆顶元素值。(即整个堆中最大或最小值)

  3. 出堆:将堆顶元素出堆。

大根堆参考实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
struct Heap {
int heap[N], n;
void clear() { n = 0; }
void up(int p) {
while (p > 1) {
if (heap[p] > heap[p / 2]) {
swap(heap[p], heap[p / 2]);
p /= 2;
} else {
break;
}
}
}
void down(int p) {
int s = p * 2;
while (s <= n) {
if (s < n && heap[s] < heap[s + 1]) ++s;
if (heap[s] > heap[p]) {
swap(heap[s], heap[p]);
p = s;
s = p * 2;
} else {
break;
}
}
}
void insert(int val) {
heap[++n] = val;
up(n);
}
void pop() {
heap[1] = heap[n--];
down(1);
}
};

但在 C++ STL中,我们有 priority_queue 。所以我们通常就直接使用它,而不会手敲二叉堆(但是至少应该知道优先队列的工作原理)。

请注意 priority_queue 的堆顶默认是整个堆中的最大值(即默认大根堆,反过来的叫小根堆)。

priority_queue 有以下方法:

  1. push(val): 将 val 插入堆。$O(logN)$
  2. top(): 查看堆顶元素值。(即整个堆中最大或最小值)$O(1)$
  3. pop(): 将堆顶元素出堆。$O(logN)$
  4. size(): 取堆大小。$O(1)$
  5. empty(): 判断堆是否为空。$O(1)$

有时我们需要(重新)定义小于号以使用 priority_queue 。有很多种方法可以实现对小于号的重载,如

  1. bool operator<
  2. friend bool operator<
  3. bool operator()

以下是练习题:

POJ-1338, HDOJ-4006, POJ-3253, HDOJ-1873, HDOJ-1509, POJ-1442, HDOJ-1896, POJ-1456

另外,与二叉堆关系密切的一个经典问题是: 哈夫曼树 (Huffman Tree). 如 T^TOJ-3810.

部分练习题参考代码

POJ-1338 [Ugly Numbers] 参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <queue>

using namespace std;

typedef long long LL;
const int N = 1e5 + 10;

priority_queue<LL, vector<LL>, greater<LL> > q;
vector<LL> v;

int main() {
int n;
q.push(1);
while (q.size() < 1500) {
LL val = q.top();
q.pop();
if (!v.empty() && v.back() == val) continue;
v.push_back(val);
q.push(val * 2);
q.push(val * 3);
q.push(val * 5);
}
while (~scanf("%d", &n) && n) {
printf("%lld\n", v[n - 1]);
}
return 0;
}

HDOJ-4006 [The kth great number] 参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <queue>

using namespace std;

int main() {
int n, k, num;
char op[3];
while (~scanf("%d %d", &n, &k)) {
priority_queue<int, vector<int>, greater<int> > q;
while (n--) {
while (q.size() > k) q.pop();
scanf("%s", op);
if (op[0] == 'I') {
scanf("%d", &num);
q.push(num);
} else {
printf("%d\n", q.top());
}
}
}
return 0;
}
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×