堆:通常是一个可以被看做一棵完全二叉树的数组对象
堆的构建(以大顶堆为例)
一.类的构建:
class Heap{
private:
int *array;//int型指针,用来开辟数组空间
int nums;//数组中长度的个数
//less用来比较大小关系
bool less(int i,int j){
.....
}
//上浮操作
void Swim(int k){
.....
}
//下沉操作
void Sink(int k,int N){
.....
}
public:
//Heap的构造函数
Heap(int nums){
array=new int[nums+1];//开辟空间
this->nums=nums;//记录数组长度
}
//构建大顶堆
void Input(){
//跳过了0号位,从1号位开始存储。
for(int i=1;i<=this->nums;i++){
cin>>array[i];
Swim(i);//边输入边上浮
}
}
//输出数组内容
void Out(){
for(int i=1;i<=this->nums;i++){
cout<<array[i]<<" ";
}
}
//进行堆排序
void Sort(){
.....
}
};
相关关键的操作:
上浮:将大的元素朝顶部移动
示意图
实现代码
//上浮操作实现代码
//k代表在数组中的第几位
void Swim(int k){
//k不需要到第1位,因为那样会和第0位发生一次比较,可能造成值丢失
while(k>1&&!less(k,k/2)){
//k位和它的双亲节点进行比较
//!less(k,k/2)在array[k]>=array[k/2]是返回true
swap(array[k],array[k/2]);
k/=2;
}
}
下沉:将小的元素朝底部移动
示意图
实现代码
//下沉操作
//k代表数组中的第几位,N代表堆的底部,即完全二叉树最后一排从左往右数的最后一个所对应在数组的下标
void Sink(int k,int N){
//2*k代表k的左儿子
while(2*k<=N){
int j=2*k;
//j<N的作用是过滤掉k无右儿子的情况
//比较左儿子和右儿子的大小,选出二者中最大的
if(j<N&&less(j,j+1)) j++;
//如果下方没有比当前节点小的节点了,就下沉停止,跳出循环
if(array[k]>array[j]) break;
//交换两个节点的值
swap(array[k],array[j]);
k=j;
}
}
堆排序核心代码
void Sort(){
//记录堆的底部下标
int len=nums;
while(len>1){
//将最大的元素固定在数组最后一位
swap(array[1],array[len]);
//将堆缩小,即排除最大的元素,然后对堆顶进行下沉
Sink(1,--len);
}
}
堆的时间复杂度:最坏最好的时间复杂度是nlogn