堆排序

来源:互联网 发布:王健林要被清算了知乎 编辑:程序博客网 时间:2024/05/20 20:58

在堆排序之前,首先得了解几个东西。

首先是满二叉树:一个二叉树所有的分支节点都存在左子树与右子树,而且所有的叶子节点都在同一层上,则这个二叉树就是满二叉树。

然后是完全二叉树:对一颗二叉树进行层序编号,如果编号为i的节点与同样深度的满二叉树中编号为i的节点的位置完全相同,则这个二叉树就是完全二叉树。

第一个就是完全二叉树的性质。为了说明完全二叉的性质的时候,这里只写出层序符号。下面就是一颗完全二叉树:

                                        1                           2                                3                   4                5               6                7             8         9      10        11      12             
n个节点的完全二叉树,深度为[log n ] +1,按层编号后,查看上面这个完全二叉树,得出每个节点的编号i有这几条性质:

1、当i=1的时候,节点i是二叉树的根,没有双亲节点,如果i>1,则双亲节点是[ i/2 ]。

2、如果2i>n,则节点i无左子树,否则只能是这个节点的左子树为2i。

3、如果2i+1>n,则节点i无右孩子,否则只能是这个节点的右子树为2i+1。

有了上面的概念后,下面来看堆的概念。


所谓的堆就是一种完全二叉树,每个节点的值都大于等于左右子树节点的值,称为大顶堆,或者每个节点小于等于左右子树节点的值,称为小顶堆。

所以节点值满足:K[i]>=K[2i+1] && K[i]>=K[2i] 或者 K[i]<=K[2i+1] && K[i]<=K[2i]。这里的i的取值为1到[n/2]。

接下来是堆排序的思想:

待排序的数组元素我们以大顶堆的方式存入,排序的时候,最大元素就是根节点,将根节点与末尾元素进行互换,并将末尾元素移走,得到元素中最大值,然后调整剩下n-1个节点,重新调整为大顶堆,继续与末尾元素互换,再移出。一直这样执行,最后获得的序列就是有序的。

现在问题来了。。。就是怎么构造一个堆呢?

这里我们构造大顶堆,故而每个节点i满足K[i]>=K[2i+1] && K[i]>=K[2i] 。下面查看从begin起始到end结束的大顶堆构建,这里需要注意的,我们严格按照数组下标的方式进行(0,1,2,3,4......),故而左右节点的坐标分别为2i+1,2i+2。

void heapcreat(int arr[],int begin,int end){int temp,j;temp=arr[begin];for(j=2*begin+1;j<=end;j=j*2){if(j<end && arr[j] < arr[j+1])++j;if(temp>=arr[j])break;arr[begin]=arr[j];begin=j;}arr[begin]=temp;}

begin的下一层,如果未超出end的范围,则需要找出下一层中最大的值(也就是1处的代码),并且记住标记j,比较处的值与temp大小。两种情况进行处理:

1、如果比j处的值大,则满足大顶堆的条件,退出循环,此处的值并不进行交换。

2、如果比j处的值小,则将这一层找出的最大值赋值给begin处元素,然后新的起始点开始于这层的最大值j,循环找到满足条件的j后,进行交换。

为了便于理解,参考下面的图:



知道上面怎么调整一个大顶堆,下面我们来看堆的排序,分为堆的创建与堆的排序。代码如下:

void heapsort(int arr[],int length){int i;int end;end = length-1;for(i=end/2;i>=0;i--)heapcreat(arr,i,end);for(i=end;i>0;i--){swapdata(arr,0,i);heapcreat(arr,0,i-1);}}
堆的创建主要是第一个for循环,由于heapcreat步长每次增加两倍,则这里从end/2开始,完成堆的创建。

然后第二个for循环完成堆的排序,构建完成大顶堆后,根元素,也就是i=0的时候,是最大元素,将此元素交换到数组的末位。然后再调整0~i-1,使其成为大顶堆(其实这里只需要一次调整即可完成),再交换,再调整。。。最后完成堆的排序。

完整的测试代码如下:

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<time.h>#define ARR_LEN 1000void swapdata(int arr[],int i,int j){int temp;temp=arr[i];arr[i]=arr[j];arr[j]=temp;}void print_arr(int arr[],double time,int length){int i;for(i=0;i<length;i++)printf("%d\t",arr[i]);printf("\nrunning time:%.10lf s\n",time);}void heapcreat(int arr[],int begin,int end){int temp,j;temp=arr[begin];for(j=2*begin+1;j<=end;j=j*2){if(j<end && arr[j] < arr[j+1])++j;if(temp>=arr[j])break;arr[begin]=arr[j];begin=j;}arr[begin]=temp;}void heapsort(int arr[],int length){int i;int end;end = length-1;for(i=end/2;i>=0;i--)heapcreat(arr,i,end);for(i=end;i>0;i--){swapdata(arr,0,i);heapcreat(arr,0,i-1);}}int main(){srand((unsigned int)time(NULL));int a[ARR_LEN],i;    double  time=0;time_t begin,end;for(i=0;i<ARR_LEN;i++){a[i]=rand()%1000;}    begin = clock();    heapsort(a,ARR_LEN);end = clock();        time=double(end - begin)/CLOCKS_PER_SEC;print_arr(a,time,ARR_LEN);}
这里对排序的时间复杂度为O(n*logn)


 


0 0
原创粉丝点击