使用场景
某些情况下要让高优先级的元素先出队,优先队列实际上是用堆实现的。
C 实现
priority_queue
优先队列
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define swap(a, b) { \
__typeof(a) __temp = a; \
a = b; b = __temp; \
}
typedef struct priority_queue {
int *data;
int cnt, size;
} priority_queue;
priority_queue *init(int n) {
priority_queue *q = (priority_queue *) malloc(sizeof(priority_queue));
q->data = (int *) malloc(sizeof(int) * (n + 1));
q->cnt = 0;
q->size = n;
return q;
}
int empty(priority_queue *q) {
return q->cnt == 0;
}
// 堆一般使用下标 1 作为头节点
int top(priority_queue *q) {
return q->data[1];
}
// 此处优先队列实现为小顶堆,小的元素在堆顶
// O(lgN)
int push(priority_queue *q, int val) {
if (q == NULL) return 0;
if (q->cnt == q->size) return 0;
q->data[++(q->cnt)] = val;
// 自底向上调整
int ind = q->cnt;
while (ind / 2 && q->data[ind] > q->data[ind / 2]) {
swap(q->data[ind], q->data[ind / 2]);
ind /= 2;
}
return 1;
}
// O(lgN)
int pop(priority_queue *q) {
if (q == NULL) return 0;
if (empty(q)) return 0;
// 头节点出队,最后一个节点交换后自顶向下调整
q->data[1] = q->data[q->cnt--];
int ind = 1;
while ((ind * 2) <= q->cnt) {
int temp = ind, l = ind * 2, r = ind * 2 + 1;
// 选择左右节点较大向下调整
if (q->data[l] > q->data[temp]) temp = l;
if (r <= q->cnt && q->data[r] > q->data[temp]) temp = r;
// 无法继续向下调整
if (temp == ind) break;
swap(q->data[ind], q->data[temp]);
ind = temp;
}
return 1;
}
void clear(priority_queue *q) {
if (q == NULL) return;
free(q->data);
free(q);
return;
}
int main() {
// 模拟
srand(time(0));
#define max_op 20
priority_queue *q = init(max_op);
for (int i = 0; i < max_op; ++i) {
int val = rand() % 100;
push(q, val);
printf("insert %d to the priority_queue!\n", val);
}
for (int i = 0; i < max_op; ++i) {
printf("%d ", top(q));
pop(q);
}
#undef max_op
printf("\n");
clear(q);
return 0;
}
heap_sort
利用堆的性质,建立初始堆,利用堆的性质完成高效的交换排序
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define swap(a, b) {\
__typeof(a) __temp = a;\
a = b; b = __temp;\
}
// O(lgN) 向下调整
void downUpdate(int *arr, int n, int ind) {
while (ind * 2 <= n) {
int temp = ind, l = ind * 2, r = ind * 2 + 1;
if (arr[l] > arr[temp]) temp = l;
if (r <= n && arr[r] > arr[temp]) temp = r;
if (ind == temp) break;
swap(arr[temp], arr[ind]);
ind = temp;
}
return ;
}
// O(NlgN) 还需要一个先维护堆的时间
void heap_sort(int *arr, int n) {
// 技巧:数组内存位置 -1,实际以 arr[0] 进行操作 arr[1-n] 排序掉
arr -= 1;
// O(N) 先维护一个堆结构
// S=2^(i-1)*(k-i) (S为时间,i为当前层数,k为堆高度)
// S=2^k-k-1=n-lgN-1(数学公式化解,k=lgN)
for (int i = n / 2; i >= 1; --i) {
downUpdate(arr, n, i);
}
// O(NlgN) 已经具有堆的性质,接下来的每次交换 O(lgN)
for (int i = n; i > 1; --i) {
swap(arr[i], arr[1]);
downUpdate(arr, i - 1, 1);
}
}
void output(int *arr, int n) {
printf("[");
for (int i = 0; i < n; ++i) {
printf("%d ", arr[i]);
}
printf("]\n");
}
int main() {
// 排序模拟
srand(time(0));
#define max_n 20
int *arr = (int *) malloc(sizeof(int) * max_n);
for (int i = 0; i < max_n; ++i) {
arr[i] = rand() % 100;
}
output(arr, max_n);
heap_sort(arr, max_n);
output(arr, max_n);
free(arr);
#undef max_n
return 0;
}
C++ 实现
#include <bits/stdc++.h>
using namespace std;
class Heap {
private:
int *data;
int size;
// 递归方式向下调整
// 以 0 下标为头节点的操作
void update(int pos, int n) {
int lchild = 2 * pos + 1, rchild = 2 * pos + 2;
int max_value = pos;
if (lchild < n && data[lchild] > data[max_value]) {
max_value = lchild;
}
if (rchild < n && data[rchild] > data[max_value]) {
max_value = rchild;
}
if (max_value != pos) {
swap(data[pos], data[max_value]);
update(max_value, n);
}
}
public:
Heap(int length_input) {
data = new int[length_input];
size = 0;
}
~Heap() {
delete[] data;
}
int top() {
return data[0];
}
// 大顶堆实现
void push(int value) {
data[size] = value;
int current = size;
// 0 下标取 father
int father = (current - 1) / 2;
while (data[current] > data[father]) {
swap(data[current], data[father]);
current = father;
father = (current - 1) / 2;
}
size++;
}
void pop() {
swap(data[0], data[size - 1]);
size--;
update(0, size);
}
void output() {
for (int i = 0; i < size; i++) {
cout << data[i] << " ";
}
cout << endl;
}
// 使用该数据结构对数组进行堆排序时,需要将数组元素先 push 进去
void heap_sort() {
for (int i = size - 1; i >= 1; --i) {
swap(data[i], data[0]);
update(0, i);
}
}
};