您的位置:首页 > 其它

非递归方法的堆排序实现

2016-07-29 00:00 351 查看

引言

首先需要明确,如何根据父亲结点的位置得知孩子结点的位置,以及如何根据孩子结点的位置得知父亲结点的位置。

假设数列索引从0开始,如果父亲结点的索引为i,那么左孩子索引为2i+1,右孩子索引为2i+2;如果孩子结点的索引为j,那么父亲结点的索引为(j-1)/2。

堆排序的核心在于函数voidadjustdown(int*arr,inti,intend),其中第i+1个元素到最后一个元素均已满足堆结构,每次adjustdown可以使当前位置i的元素也满足堆结构。如果是大堆,则经过adjustdown后当前位置的元素最大;如果是小堆,则经过adjustdown后当前位置的元素最小。

以下代码,将数列从小到大排序。采用大堆,并且使用了非递归的方法。函数adjustdown中的五个if语句实际上有一部分是可以合并的,但是为了逻辑清晰,在本代码中,完整保留下来了。注释我写的很用心,相信读者可以看懂。

代码

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<time.h>

staticvoidshow(int*arr,intlen){intindex;for(index=0;index<len;index++){printf("%d",arr[index]);}printf("\n");}staticvoidswap(int*left,int*right){inttmp=*left;*left=*right;*right=tmp;}voidadjustdown(int*arr,inti,intend){intkey=arr[i];intp=i;intleft=2*p+1;/*越界就是没孩子*//*只要能进循环,一定有左孩子*/
while(left<=end){/*有右孩子的情况下,大于等于左右孩子不用换*/
if((key>=arr[left])&&(left+1<=end&&key>=arr[left+1])){break;}elseif(key>=arr[left]&&left+1>end)/*没有右孩子,只有左孩子,且大于等于左孩子不用换*/{break;}elseif(left+1<=end&&arr[left+1]>=arr[left]&&key<arr[left+1])/*与右孩子换。要保证有右孩子,且右孩子大于等于左孩子,父亲小于右孩子*/{swap(arr+p,arr+left+1);p=left+1;//父亲与谁换,就到谁的位置了
left=2*p+1;//父亲新的左孩子的位置
}elseif(left+1<=end&&arr[left]>arr[left+1]&&key<arr[left])/*与左孩子换。有右孩子的情况下,右孩子小于左孩子,父亲小于左孩子*/{swap(arr+p,arr+left);p=left;left=2*p+1;}elseif(left+1>end&&arr[left]>key)/*与左孩子换。没右孩子的情况下,只需父亲小于左孩子*/{swap(arr+p,arr+left);p=left;left=2*p+1;}}}voidheap_sort(int*arr,intlen){intp;//最后一个父亲
intend;//最后一个有效下标
/*建一个大顶堆,从最后一个父亲开始调*/
for(p=(len-1-1)/2;p>=0;p--){adjustdown(arr,p,len-1);}/*根结点的值最大,与末尾交换,并继续建立堆结构,再交换...*/
for(end=len-1;end>=1;end--){swap(arr,arr+end);//end已经是最大值
adjustdown(arr,0,end-1);//从arr+1到end-1位置都是满足堆结构的
}}intmain(intargc,char*argv[]){intindex;intarr[10];memset(arr,0,10);srand(time(NULL));for(index=0;index<10;index++){arr[index]=rand()%20+1;}show(arr,10);heap_sort(arr,10);show(arr,10);system("pause");return0;}



                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: