堆排序使用一种称为“堆”的数据结构来进行信息管理。堆不仅用在堆排序中,还可以用来构造优先队列。
堆
堆是一个数组,它可以被看成一个近似的完全二叉树,树上的每一个结点对应数组中的一个元素。
根据二叉树的性质可知,给定一个结点在数组中的下标i
双亲结点: Parent=floor(i/2);
左孩子结点: Left=i*2;
右孩子结点: Right=i*2+1;
二叉堆可以分为两种形式:
- 最大堆:双亲结点大于孩子结点
- 最小堆:双亲结点大于孩子结点
最大堆用来进行从大到小的排序,最小堆用来进行从小到大的排序。
用来构造堆的函数:
1. Max_Heapify: 其时间复杂度为O(lgn),用来调整子树
2. BUlid_Max_Heap:具有线性时间复杂度,用来将无序的输入数据构造成一个最大堆
3. HeapSort:用来对一个数组进行原址排序
堆排序
对于一个最大堆,它的所有子树也均为最大堆。对于一个高度为2的子树,我们可以通过比较双亲结点与孩子结点的大小关系,然后依次来调整位置,最后递归调用(也就是说Max_Heapify包括了将子树调整为最大堆的过程),在这个过程中,可以想象到较小值可以一直下移,而较大值最多可以移到当前递归的子树的双亲结点。
void Max_Heapify(int* A,int i,int n){
int l=Left(i);
int r=Right(i);
int largest=0;
if(l<n&&A[l]>A[i])
largest=l;
else
largest=i;
if(r<n&&A[r]>A[i])
largest=r;
if(largest!=i){
swap(A[i],A[largest]);
Max_Heapify(A,largest,n);
}
}
那么如何让较大值一直上移呢?我们可以从最底层的子树开始调整,从下往上调用Max_Heapify,这样操作的子树必然为最大堆。
Build_Max_Heap将较大值不断上移,Max_Heapify每一次调整都可以将较小值不断下移。
void Build_Max_Heap(int* A,int n){
for(int i=floor(n/2);i>=1;i--)
Max_Heapify(A,i,n);
}
#include<iostream>
#include<cmath>
#include<cstdlib>
using namespace std;
void HeapSort(int* A,int n);
int Parent(int i);
int Left(int i);
int Right(int i);
void Max_Heapify(int* A,int i,int n);
void Build_Max_Heap(int *A,int n);
int main(){
int n;
cin>>n;
int *A=new int[n];
for(int i=0;i<n;i++)
A[i]=rand();
HeapSort(A,n);
for(int i=0;i<n;i++){
cout<<A[i]<<" ";
}
delete A;
}
void HeapSort(int* A,int n){
Build_Max_Heap(A,n);
for(int i=n-1;i>=1;i--){
swap(A[0],A[i]);
n=n-1;
Max_Heapify(A,1,n);
}
}
int Parent(int i){
return floor(i/2.0);
}
int Left(int i){
return 2*i;
}
int Right(int i){
return 2*i+1;
}
void Max_Heapify(int* A,int i,int n){
int l=Left(i);
int r=Right(i);
int largest=0;
if(l<n&&A[l]>A[i])
largest=l;
else
largest=i;
if(r<n&&A[r]>A[i])
largest=r;
if(largest!=i){
swap(A[i],A[largest]);
Max_Heapify(A,largest,n);
}
}
void Build_Max_Heap(int* A,int n){
for(int i=floor(n/2);i>=1;i--)
Max_Heapify(A,i,n);
}