/*
算法名称:堆排序
时间:20180419
编写者:发烧的小龙虾
编写动机:很重要呗
*/
#include <bits/stdc++.h>
using namespace std;
/*建立大顶堆的过程,将数组的i~n这些元素建成大顶堆*/
/*已知a[i]~a[n]中,a[i]是根,除了a[i]其他元素均满足大顶堆定义,因为建堆的过程是自下而上进行的,也就是说进行到这一步,除了根节点之外,它的左子树和右子树肯定都已经建立好了,只需要将a[i]这个元素逐层进行比较大小就行,如果a[i]小于下面某一层的某个节点,就交换信息,但函数并未用到交换,而是巧妙的利用了一种算法来进行*/
void HeapAdjust(int a[],int i,int n)
{
int k,temp;//k为试探点的下标,即为a[i]的左孩子或者右孩子下标,temp保存最初a[i]的值,因为a[i]会变动,因此要用temp来保存
temp=a[i];//保存最初a[i]的值
for(k=2*i;k<=n;k*=2)//k=2*i,此时k在a[i]的左孩子上,先要判断有没有右子树,如果有,是左边大还是右边大?k跳到大的那个子树上
{
if(k+1<=n&&a[k]<a[k+1])//如果右子树存在且右边大,k++,代表k跳到a[i]的右子树上,即此时k=2*i+1
k++;
if(a[k]>temp)//判断这个子树和temp的关系,注意这里是擂台赛,每轮比拼的时候看的是最初始的a[i],我是最初始a[i]一层一层的进行擂台赛,如果发现大于左右子树就退出擂台赛,只要有子树比a[i]大,就必须继续进行擂台赛,而这里没有交换,而是让a[i]=a[k],a[i]此时不再是最初始的根了,而是当前的根
{
a[i]=a[k];//a[i]表示当前的根,不是最初始的根了。当前的根变为a[k](因为a[k]更大)
i=k;//然后i指针下移指向k,此时的a[i]为更新后的根,它又要和它的左右孩子进行比较,一直循环下去
}
else//如果发现比左右孩子中最大的那个还要大,就停止了,因为再往下也不可能有比a[i]更大的了,因为是从下往上建立的,除了最初始的a[i],越往上越大。
break;
}
a[i]=temp;//最后一个a[i]就填回temp
}
/*堆排序算法,升序!*/
void HeapSort(int a[],int n)
{
int i;
for(i=n/2;i>=1;i--)//从n/2逆序,即从下往上建立大顶堆
HeapAdjust(a,i,n);
for(i=n;i>=2;i--)//每次都拿最后一个元素开刀,而且每轮循环之后,需要建堆的元素个数就会少一个(因为最后一个一定是最大的,我把大顶堆的根放到最后一个了),因此i从n开始循环到2
{
swap(a[1],a[i]);//每次都让他和最后一个元素交换
HeapAdjust(a,1,i-1);//此时,最后一个元素一定是最大的,只用关心前n-1个元素就行了,相应的元素个数也在减少,编变成i-1了。这次这句话的意思是从第一个元素到i-1个元素进行建立大顶堆操作。
}
}
int main()
{
int a[100],i,n;
while(scanf("%d",&n)!=EOF)
{
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
HeapSort(a,n);
for(i=1;i<=n;i++)
printf("%d ",a[i]);
printf("\n");
}
}