首先给出堆的定义(这里转自https://blog.csdn.net/u011068702/article/details/52771173)
n个元素的序列{k1,k2,…,kn}当且仅当满足下列关系之一时,称之为堆。
情形1: <= 且 <= (最小化堆或小顶堆)
情形2: >= 且 >= (最大化堆或大顶堆)
其中i=1,2,…,n/2向下取整;
这边两个定义是由序号从0开始取还是从1开始取决定的。
说明一下定义的意思,因为堆是完全二叉树(官方定义是:若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树),其实很简单即只有最后一层可以不全,其他各层节点都是满的,而且就算最后一层不全,也不能出现只有右孩子而没有左孩子的情况,即所有结点都连续集中在最左边。
而且二叉树的序列化即写成数组形式用的是层遍历,因此有个很好的性质即第i个节点的左右孩子序号分别为2i和2i+1,而且最后一个有孩子的节点序号为n/2,n为节点个数。(这里序号从1开始)
因此在构建大根堆的时候我们可以从最后一个有孩子的节点序号开始向前遍历,如果该节点比它的孩子小就交换(只需要考虑当前节点和他的孩子)。
而堆排序很巧妙,首先构建好大根堆,然后我们拿出头节点和最后一个节点交换,因为头节点一定是最大的,所以现在已经将最大的值放入数组最后了,然后使用除了最后一个元素的数组再次构建大根堆,这样循环下来就可以将数组排序。代码如下:
#include<iostream>
using namespace std;
class HeapSort {
public:
void heapSort(int* A, int n)
//堆排序
{
int i,temp;
for(i=n/2-1;i>=0;--i)//从最后一个有孩子的节点开始循环来构建大根堆
HeapAdjust(A,i,n-1);
for(i=n-1;i>0;i--)//交换头节点和第i个元素,并使用前i个元素构建新的大根堆
{
temp=A[0];
A[0]=A[i];
A[i]=temp;
HeapAdjust(A,0,i-1);
}
}
void HeapAdjust(int *A,int s,int m)
//已知A[s,...,m]中记录的关键字除A[s]之外均满足堆的定义,本函数调整A[s]
//的关键字,使A[s,...,m]成为一个大顶堆(对其中记录的关键字而言)
{
int j,rc=A[s];
for(j=2*s+1;j<=m;j=2*j+1)
{
if(j<m&&A[j]<A[j+1])j++;//右孩子更大
if(rc>A[j])break;//比孩子大则跳出
A[s]=A[j];
s=j;
}
A[s]=rc;
}
};
int main()
{
int arr[]={54,35,48,36,27,12,44,44,8,14,26,17,28};
HeapSort a;
a.heapSort(arr,13);
for(int i=0;i<13;i++)
cout<<arr[i]<<" ";
cout<<endl;
return 0;
}