算法介绍:
堆可以看成是一棵完全二叉树,满足条件,非叶结点都不大于或者不小于其左右孩子结点的值。若是双亲大孩子小,则为大根堆;双亲小,孩子大,则为小根堆。
可以得知,堆的根结点是这棵树的最大值或者最小值,可以将一个无序序列建立一个堆,取出根结点,换到队尾或者队首,那么无序序列中无序关键字减少一个,重复前面的步骤。
堆排序的过程就是不断的调整树,使其满足符合上述定义的树。
执行过程:
次序列 49 38 65 97 76 13 27 49 为 例
初始建堆
首先将初始堆调整为大根堆 49 76 13 27 是叶子结点 所以调整从他们的父亲结点开始
97、65满足定义,调整38
38的俩孩子结点都不满足,则和较大者交换,与97交换后,与97左孩子不满足定义,交换38 49 得到如图所示
调整49
49<97 交换,交换后,49<76仍然不满足,再交换49与76 得到如下图
初始堆调整完毕
将根结点97与序列的最后一个关键字38交换,第一趟排序完成,97达到其最终位置。
调整除97外的序列,此时只有38不满足定义,只需调整根结点即可。
调整38 与 76 换, 仍然不满足,与49换,完成
继续将76和27交换,重复以上步骤。直至只剩下一个结点。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<string>
using namespace std;
#define ll long long
#define MAXN 10000+5
void Init(int a[],int n)
{
for(int i=1;i<=n;i++)
cin>>a[i];
}
void Show(int a[],int n)
{
for(int i=1;i<=n;i++)
cout<<a[i]<<" ";
cout<<endl;
}
void Sift(int a[],int low,int high)
{
int i=low,j=2*i; //j为i的左孩子
int temp=a[i];
while(j<=high)
{
if(j<high&&a[j]<a[j+1]) //右孩子大,则把j指向右孩子
++j;
if(temp<a[j])
{
a[i]=a[j]; //调整a[j]到双亲节点的位置上
i=j;
j=2*i;
}
else
break;
}
a[i]=temp; //最终位置
}
void heapSort(int a[],int n)
{
int temp;
for(int i=n/2;i>=1;i--) //初始堆
{
Sift(a,i,n);
}
for(int i=n;i>=2;i--)
{
swap(a[i],a[1]); //换出根节点中的关键字
Sift(a,1,i-1);
}
}
int main()
{
int n;
int a[MAXN];
while(cin>>n)
{
Init(a,n);
heapSort(a,n);
Show(a,n);
}
return 0;
}
/*
2
3 2
4
4 3 2 1
8
1 3 2 4 5 8 7 9
*/