复杂度:
O(nlog(n))
堆是一个数组,可以看做近似的完全二叉树;
对于给定的下标 i :
父节点的下标:i/2
左子节点的下标:2i
右子节点的下标:2i+1
最大堆:
除了根以外的结点满足:
a[Parent(i)] >= a[i]
维护堆的性质:
宏定义:
# define PARENT(i) (i/2)
# define LEFT(i) (2*i)
# define RIGHT(i) (2*i + 1)
多说一句,这里如果用堆排序的话,下标要从1开始了,因为如果i是0,LEFT(i)也是0,而左子节点应该是1,就会出现错误,所以数组下标的范围为 1 到 heap_size
MAX-HEAPIFY函数:
void MaxHeapify(int* a, int i, int heap_size) {
int maxn = i;
if (LEFT(i) <= heap_size && a[LEFT(i)] > a[i]) {
maxn = LEFT(i);
}
if (RIGHT(i) <= heap_size && a[RIGHT(i)] > a[maxn]) {
maxn = RIGHT(i);
}
if (maxn != i) {
swap(a[i], a[maxn]);
MaxHeapify(a, maxn, heap_size);
}
}
程序第9行,如果我们一开始就是一个局部的最大堆,就满足第一个元素最大,我们是不会递归向下去维护堆的性质的
建堆:
a[(n/2)+1]到a[n-1]的元素是叶子节点;
对所有非叶子节点,调用一次MaxHeapify():
void BuildMaxHeap(int* a, int heap_size) {
for (int i = heap_size / 2; i >= 0; i--)
MaxHeapify(a, i, heap_size);
}
复杂度:
这里直观理解的话,建一个最大堆貌似是
O(nlog(n))
但是经过P88的运算就神奇变成了
O(n)
堆排序:
void HeapSort(int* a, int heap_size) {
BuildMaxHeap(a, heap_size);
for (int i = heap_size; i > 1; i--) {
swap(a[1], a[i]);
heap_size--;
MaxHeapify(a, 1, heap_size);
}
}
优先队列:
四种操作:
INSERT(S,x):把元素x插入到S中
MAXIMUM(S):返回S中具有最大键字的元素
EXTRACT-MAX(S):去掉并返回S中具有最大键字的元素
INCREASE-KEY(S,x,k):将元素x的关键字值增加到k, 这里假设k的值不小于x的原关键字值
MAXIMUM(S):
int HeapMaximum(int* a) {
return a[1];
}
EXTRACT-MAX(S):
int HeapExtractMax(int* a, int heap_size) {
if (heap_size < 1) {
cout << "Heap Underflow\n";
return;
}
int maxn = a[1];
a[1] = a[heap_size];
heap_size --;
MaxHeapify(a, 1);
return maxn;
}
INCREASE-KEY(S,x,k):
void HeapIncreaseKey(int* a, int i, int k) {
if (k < a[i]) {
cout << "Wrong Input\n";
return;
}
a[i] = k;
while(i > 1 && a[PARENT(i)] > a[i]) {
swap(a[i], a[PARENT(i)]);
i = PARENT(i);
}
}
INSERT(S,x):
先开一个节点在优先队列的末尾,关键字赋为负无穷,然后将这个节点的关键字增加到x
void MaxHeapInsert(int* a, int x, int heap_size) {
heap_size = heap_size + 1; //这里要保证不会溢出
a[heap_size] = -MAXN;
HeapIncreaseKey(a, heap_size, x);
}