堆实际上是一棵完全二叉树,其任何一非叶节点满足性质:
Key[i]<=key[2i]&&Key[i]<=key[2i+1]或者Key[i]>=Key[2i]&&key>=key[2i+1]
即任何一非叶节点的关键字不大于或者不小于其左右孩子节点的关键字。
堆分为大顶堆和小顶堆,满足Key[i]>=Key[2i]&&key>=key[2i+1]称为大顶堆,满足 Key[i]<=key[2i]&&Key[i]<=key[2i+1]称为小顶堆。由上述性质可知大顶堆的堆顶的关键字肯定是所有关键字中最大的,小顶堆的堆顶的关键字是所有关键字中最小的。
堆排序的思想:
总体概括就是:(1)构造初始堆(小顶堆或大顶堆,注意调整堆);(2)进行排序(此间也要注意调整堆);
调整堆(不符合所构造顶特性的进行调整,构造与排序都要):
步骤:(1)将数组转化为完全二叉树,对非叶子节点往大顶堆(小顶堆)进行调整
/*堆的调整*/
void HeapAdjust(int a[],int i,int size){
int lchild = 2*i; //下标从1开始;对应左节点 (如果下标从0开始,可设为 (lchild = 2*i+1,rchild = 2*i+2) )
int rchild = 2*i+1; //对应右节点
int max = i; //记录求某一节点对应的最大值的下标;
/*调整只针对非叶子节点,易知非叶子节点的最大下标为size/2*/
if(i<=size/2){
/*即每次调整都是从父节点、左孩子节点、右孩子节点三者中选择最大者跟父节点进行交换
(交换之后可能造成被交换的孩子节点不满足堆的性质,因此每次交换之后要重新对被交换的孩子节点进行调整)。*/
if(lchild<=size&&a[lchild]>a[max]){
max = lchild;
}
if(rchild<=size&&a[rchild]>a[max]){
max = rchild;
}
if(max!=i){
swap(a[i],a[max]); //于最大值交换
HeapAdjust(a,max,size); //(交换之后可能造成被交换的孩子节点不满足堆的性质,因此每次交换之后要重新对被交换的孩子节点进行调整)
}
}
}
(2)构造初始堆完后,可以进行排序了:
先把构造完的堆贴出来:
/*建立最大堆*/
void BuildHeap(int a[],int size){
/*针对非叶子节点,非叶节点最大下标值为size/2 */
for(int i = size/2;i>=1;i--){
HeapAdjust(a,i,size);
}
}
排序函数:
/*堆的排序*/
void HeapSort(int a[],int size){
BuildHeap(a,size);
for(int i = size;i>=1;i--){
swap(a[1],a[i]) ;
/*交换堆顶和最后一个元素,即每次将剩余元素中的最大者放到最后面(间接将最大者从堆中删除 (HeapAdjust函数对应的size减1))
重新调整堆顶节点成为大顶堆*/
HeapAdjust(a,1,i-1);
}
}
完整代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
/*堆的调整*/
void HeapAdjust(int a[],int i,int size){
int lchild = 2*i; //下标从1开始;对应左节点 (如果下标从0开始,可设为 (lchild = 2*i+1,rchild = 2*i+2) )
int rchild = 2*i+1; //对应右节点
int max = i; //记录求某一节点对应的最大值的下标;
/*调整只针对非叶子节点,易知非叶子节点的最大下标为size/2*/
if(i<=size/2){
/*即每次调整都是从父节点、左孩子节点、右孩子节点三者中选择最大者跟父节点进行交换
(交换之后可能造成被交换的孩子节点不满足堆的性质,因此每次交换之后要重新对被交换的孩子节点进行调整)。*/
if(lchild<=size&&a[lchild]>a[max]){
max = lchild;
}
if(rchild<=size&&a[rchild]>a[max]){
max = rchild;
}
if(max!=i){
swap(a[i],a[max]); //于最大值交换
HeapAdjust(a,max,size); //(交换之后可能造成被交换的孩子节点不满足堆的性质,因此每次交换之后要重新对被交换的孩子节点进行调整)
}
}
}
/*建立最大堆*/
void BuildHeap(int a[],int size){
/*针对非叶子节点,非叶节点最大下标值为size/2 */
for(int i = size/2;i>=1;i--){
HeapAdjust(a,i,size);
}
}
/*堆的排序*/
void HeapSort(int a[],int size){
BuildHeap(a,size);
for(int i = size;i>=1;i--){
swap(a[1],a[i]) ;
/*交换堆顶和最后一个元素,即每次将剩余元素中的最大者放到最后面(间接将最大者从堆中删除 (HeapAdjust函数对应的size减1))
重新调整堆顶节点成为大顶堆*/
HeapAdjust(a,1,i-1);
}
}
int main(int argc, char** argv) {
int a[1000];
int size;
printf("输入数组大小:\n");
scanf("%d",&size) ;
printf("输入数组元素:\n");
for(int i = 1;i<=size;i++) {
scanf("%d",&a[i]) ;
}
HeapSort(a,size);
printf("堆排序结果为:");
for(int i = 1;i<=size;i++) {
printf("%d ",a[i]);
}
return 0;
}