堆(本文以大根堆为例)
堆分为大根堆和小根堆
大根堆的性质:
- 完全二叉树
- 时间复杂度为O(logn)
- 父亲节点的值大于等于儿子节点的值
- 以数组形式存储(下标从1开始)的大根堆:左儿子的位置=父亲的位置 * 2,
- 右儿子的位置=父亲的位置 * 2-1
如图(节点数值表示位置)
大根堆的操作:
- 建堆
- 取出最大值
- 插入数值
建堆
- 从最后一个父亲的位置(size/2)开始:若父亲的值小于两个儿子中的最大值,则交换父亲与此儿子的位置
- 逐个检查父亲的情况,直到根节点
举个栗子:
void HeapAdjust() {
int n = size;
//初始选择最后一个父亲位置
int p = n / 2;
while (p > 0) {
//选择数值较大的孩子的位置
int max;
if (!heap[2 * p + 1]) max = 2 * p;
else max = heap[2 * p] > heap[2 * p + 1] ? 2 * p : 2 * p + 1;
//孩子和双亲比较,若孩子大则交换位置
if (heap[p] < heap[max]) {
int temp = heap[p];
heap[p] = heap[max];
heap[max] = temp;
}
p--;
}
}
插入数值:
- 首先在堆的末尾插入该数值
- 不断向上提升直到大小满足性质为止(因为大部分的数据满足性质,不需要整个调整堆)
举个栗子:
void push(int x) {
size++;
int n = size;
heap[n] = x;
int p = n / 2;
while (p > 0) {
if (heap[n] < heap[p]) break;
else {
heap[n] = heap[p];
n = p;
p /= 2;
}
}
heap[p] = heap[n];
}
取出最大值:
- 首先删除堆头的最大值
- 堆尾元素填补到堆头
- 不断向下交换,直到满足性质(需要对整个堆进行调整)
举个栗子:
int pop() {
int ans;
ans = heap[1];
heap[1] = heap[size--];
HeapAdjust();
return ans;
}