详情见代码中的注释
/*
* 堆的性质
* 堆中某个节点的值总是不大于或不小于其父节点的值(小顶堆或大顶堆)
* 堆总是一棵完全二叉树
*
* 由于堆是一个完全二叉树,所以我们可以用数组模拟树
* 第i个结点的父结点是第i/2个结点
* 第i个结点的左子女是第2*i个结点
* 第i个结点的右子女是第2*i+1个结点
*
* 堆的应用
* 堆排序 优先队列
*/
//-------------------
/*
* 维持堆的基本性质
* 假定pos的左子树和右子树都已经是一个堆
* 然后调整pos,使它满足堆的性质
*/
void Heapify(int a[N], int length, int pos){
int left = pos*2;//左子女
int right = pos*2+1; //右子女
int largest = pos;
// 在pos,left,right中寻找最大值
if (length >= left){
largest = a[largest] >= a[left] ? largest : left;
}
if (length >= right){
largest = a[largest] >= a[right] ? largest : right;
}
if (largest != pos){
swap(a[largest], a[pos]); //交换最大值
Heapify(a, length, largest); //交换后子树有可能不满足堆的性质
}
}
/*
* 建立一个堆
* 将数组a转换成一个大顶堆
* 先找到最后一个非叶子结点(即第n/2个结点,因为堆是一个完全二叉树)
* 从这个结点开始,从后往前调整每个子树,使之满足堆的性质
* 最后整个数组都满足堆的性质
* 因为a[n/2+1...n]都是叶子结点,可以看做只有一个结点的堆
*/
void BuildHeap(int a[N], int length){
for (int i = length/2; i > 0; i--){
Heapify(a, length, i);
}
}
/*
* 堆排序算法
* 先构造一个大顶堆,此时a[1]是最大值,
* 把它和a[length]交换,然后将length减一,再调整堆
*
* 如果要升序,则建立一个大顶堆
* 如果要降序,则建立一个小顶堆
*
*/
void HeapSort(int a[N], int length){
BuildHeap(a, length);
for (int i = length; i > 0; i--){
swap(a[i], a[1]);
Heapify(a, i-1, 1);
}
}
/*
* 优先队列 priority_queue
*
* 删除队列中的最大值
* 参数说明:数组a已经是一个堆
* a[1]就是最大值
* 将a[length]赋给a[1],然后length减一
* 调整堆
*/
void DelMax(int a[], int &length) {
a[1] = a[length];
length--;
Heapify(a, length, 1);
}
/*
* 获取队列的最大值
*/
int GetMax(int a[], int length) {
return a[1];
}
/*
* 插入
* 向堆中插入一个元素value, 同时仍然保持堆的性质
* 将value放在最后面,然后从下向上调整,使a仍保持堆的性质
*/
void insert(int a[], int &length, int value) {
length++;
int pos = length;
while(pos > 1 && a[pos/2] < value){
a[pos] = a[pos/2];
pos /= 2;
}
a[pos] = value;
}