堆是一种数据结构,可以把他看作是一个完全二叉树,这个二叉树满足任何一个父结点都不大于或小于其左右孩子的值。若父亲大而孩子小,则叫做大顶堆,相反的叫做小顶堆。
在应用堆排序时,可以选择大顶堆或小顶堆,当使用大顶堆时排序出来的序列时非递减的(非递减不等于递增,因为其可能有相同的排序关键字),以下的代码采用了大顶堆的方式。
void void Sift(int low,int hight,int r[]){
int i=r[low];
j=low*2;
while(j<=hight&&temp<r[j]){
if(r[j]<r[j+1]){
j++;
}
if(temp<r[j]){
i=j;
j=i*2;
}
else//当temp>r[j]时,立即结束了整个操作,这个操作时可以理解为从父节点不断向下降落,即从上到下逐级比较。
break;
}
r[i]=temp;
}
//以下时整个儿的heapsort的过程。
void HeapSort(int r[],int n){
int i;
int temp;
for(int i=n/2;i>=1;i--){//开始是从叶子节点的父节点开始的。
sift(i,n,r);//是自下向顶开始的
}//此时决出了最大值,其位于根节点上
for(int i=n;i>=2;i--){
temp=r[1];
r[1]=r[i];
Sift(r,1,i-1);
}//当执行第一次循环是,是将父节点放在了序列的最后一个,第一次的排序完成,那么第二次排序是将序列的倒数第二个放入根节点。决出了第二大,放入倒数第二个位置。
//注意以上循环都是自顶向下逐级比较的(开头Sift的调用结束后保证了每一级的父节点一定大于其左右孩子)所以第二次的sift的调用一定决出了最大值
}
堆排序算法的时间复杂度:对于sift函数其代表从某一节点走向了其叶子节点,完全二叉树的高度log2(n+1)(还要进行取整),所以对每个节点进行调整的时间复杂度就是O(log2n),对于heapsort函数来说其时间复杂度就是其两个并列循环的算法时间复杂度的之和,第一个循环的算法时间复杂度是O(log2n)*n/2,第二个循环的算法时间复杂度是O(log2n)*(n-1),所以对整个函数来说就是O(nlog2n)。
不论是是最好还是最坏的情况都是一样的时间复杂度。
其优点,其空间复杂度较小为O(1),并且其适用于有较多的关键字排序中,如从100000数据中从小到大选出前十个。
堆排序的思想:其实堆排序就是一种二叉选择树,通过一个较大的时间复杂度算法对堆进行初始化,形成一个满足要求的堆,之后用新的关键字替代原来关键字的位置,这样就只会对树上的一点结构进行改变,而树的整体结构不会大变。所以就不用重新建树,只需要进行局部的调整。