二叉堆:二叉堆一棵完全二叉树,从递归的定义来讲,对于完全二叉树的任何一个节点,其左孩子要么是空树要么是一个完全二叉树,右孩子同上。
堆:对于一个堆来讲,可以是一个大根堆,也可以是一个小根堆。
大根堆的性质:对于在大根堆任何一个节点,其值不小于左右孩子的值。 小根堆的性质:对于在大根堆任何一个节点,其值不大于左右孩子的值。 由于堆的底层数据结构是由完全二叉树实现的,就可以利用完全二叉树的一些性质来实现一个堆。假设一棵完全二叉树的编号从零开始,则对于任意节点i,其父亲节点和孩子节点可以表示为。 father(i) = i/2; left(i) = 2 * i,right(i) = 2 * i 1; 用数组构建一个堆:由于数组的下标是从0开始的,这样与完全二叉树节点从1开始不对应,实际可以这样处理,为数组多申请一个空间不使用索引为0的空间,这样就可以将一棵完全二叉树和数组完全的对应起来,这样处理会使得代码编写更为简单,代码的可读性非常高。
代码语言:javascript复制template<typename T>
class Heap{
private:
T* data;//存储堆中数据的数组
int count;//记录当前堆中有效元素的个数
int capacity;//记录堆存储的容量
//向上调整元素data[k]满足堆的性质
void shiftUp(int k){
//以构建大根堆为前提条件
while(k>1 && data[k/2] < data[k]){
//若k节点的父亲节点data[k/2]比自己还小,需交换位置
/*循环终止条件k>1,当k==2时,其父亲节点为data[1]。
需注意边界处理,否则会发生数组越界*/
swap(data[k/2],data[k]);
k /= 2;//更新节点
}
}
//向下调整data[k]以继续满足堆的性质
//对于任何一个节点,寻找max(node,max(node->left,node->right))作为当前的根节点
void shiftDown(int k){
//当前节点有右孩子
while(2*k<=count){
int j = 2*k;//j表示最终data[k]所在的位置
if(j 1 <= count && data[j 1]>data[j]){
j ;//当前节点存在右孩子,并且右孩子大于左孩子的值
}
if(data[k] > data[j])
break;//当前节点大于其左右孩子,则不需要做调整
else
swap(data[k],data[j]);
k = j;//更新新的根节点
}
}
public:
Heap(int capacity){
data = new T[capacity 1];//0索引不使用
this->capacity = capacity;
this->count = 0;
}
~Heap(){
delete[] data;//释放在堆上开辟的内存
}
int size(){return count;}
bool isEmpty(){return size==0;}
void insert(T value){
//插入数据前需判断是否还有空间可以插入
assert(count<capacity);
data[count 1] = value;
count ;
//新添加的元素可能不满足堆的性质,需做调整维护堆的性质
shiftUp(count);//调用被private限定的方法,不直接暴露给用户
}
//推出堆顶元素
T extractMax(){
//需判空,如果堆空,则没有堆顶元素
assert(count >= 1);//堆中至少由一个元素
T ans = data[1];//对于大根堆,data[1]就是此堆的最大元素
swap(data[1],data[count]);//将堆中最后一个元素和第一个元素交换位置
count --;//删除被交换下来的最大元素
shiftDown(1);//data[count]放置到第一个位置有可能不满足堆的性质,需做调整
return ans;
}
};
利用上边已经实现的Heap的类模板实现堆排序
代码语言:javascript复制#include <iostream>
using namespace std;
//堆排序的接口
template<typename T>
void heapSort(T arr[],int n){
Heap<T> hp(n 1);
for(int i=0;i<n; i){
hp.insert(arr[i]);
}
//从小到大排序
for(int i=n-1;i>=0;--i){
arr[i] = hp.extractMax();
}
}
template<typename T>
void show(T arr[],int n){
for(int i=0;i<n; i){
cout<<arr[i]<<" ";
}
cout<<endl;
}
主函数
代码语言:javascript复制int main(void){
int arr[] = {8,5,9,7,3,6,4,2,1};
int n = sizeof(arr)/sizeof(int);
heapSort(arr,n);
show(arr,n);
return 0;
}
执行结果: