首页 > 其他 > 详细

堆排序

时间:2014-04-20 03:45:21      阅读:611      评论:0      收藏:0      [点我收藏+]

 

堆排序中,最初的步骤就是建立一个堆。之前在一些公司的笔试题上面见到一些与建堆过程相关的题目,因为当时对建堆过程有个误解,所以经常选错。之前一直以为是在完全二叉树中依次插入序列中的元素,每插入一个元素,就调用siftup操作;而实际的建堆操作是序列中元素首先就全部填入一个完全二叉树,然后从第一个非终端节点开始调用siftdown操作,依次调整。


堆排序过程

堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。

既然是堆排序,自然需要先建立一个堆,而建堆的核心内容是调整堆,使二叉树满足堆的定义(每个节点的值都不大于其父节点的值)。调堆的过程应该从最后一个非叶子节点开始,假设有数组A = {1, 3, 4, 5, 7, 2, 6, 8, 0}。那么调堆的过程如下图,数组下标从0开始,A[3] = 5开始。分别与左孩子和右孩子比较大小,如果A[3]最大,则不用调整,否则和孩子中的值最大的一个交换位置,在图1中是A[7] > A[3] > A[8],所以A[3]与A[7]对换,从图1.1转到图1.2。

bubuko.com,布布扣


以上便是建堆的完整过程,主要总结有以下几点需要注意:

1.首先将所有元素按照初始顺序填充到一个完全二叉树中

2.从“最后一个非终端节点”开始,调用siftdown方法,调整堆的结构,直到根节点为止



堆排序的关键是要实现siftup和siftdown。当建立完这两个函数以后,排序一个数组只需要5行代码。算法执行了n-1次siftup和siftdown,而每次操作的成本最多O(lgn),所以运行时间为O(nlogn)。

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3.   
  4.   
  5. #define MAX 20  
  6.   
  7.   
  8. void swap( int *data, int i, int j)  
  9. {  
  10. int temp = data[i];  
  11. data[i] = data[j];  
  12. data[j] = temp;  
  13. }  
  14.   
  15.   
  16. //siftup比较好理解,将每一个元素都与自己的父亲比较,如果自己的值小于父亲的值,就互换,直到到堆顶,或父亲的值小于自己的值为止。  
  17. void siftup(int *data, int n )  
  18. {  
  19. int i = n;  
  20. int p;  
  21. while( 1 )  
  22. {  
  23. if ( i == 1 ) break;  
  24. p = i/2;  
  25. if( data[p] <= data[i]) break;  
  26. swap( data, i, p );  
  27. i = p;  
  28. }  
  29. }  
  30.   
  31.   
  32. //这里的n其实意义不大,是指n之后的数据是符合要求的,n之前的数据可能不满足小根堆的要求,调整的方法也是从堆顶开始,初步向小调整  
  33. void siftdown( int *data, int n)  
  34. {  
  35. int i = 1;  
  36. int c = 0;  
  37. while( 1 )  
  38. {  
  39. c = 2 * i;  
  40. if( c > n ) break;  
  41. //取两个孩子中较小的一个与自己作比较  
  42. if( c + 1 <= n && data[ c + 1] < data[c] )  
  43. c++;  
  44.   
  45.   
  46. //如果孩子的值小于自己的值,则互换  
  47. if( data[i] <= data[c] )  
  48. break;  
  49. swap( data, c, i);  
  50. i = c;  
  51. }  
  52. }  
  53.   
  54.   
  55. int main()  
  56. {  
  57. int data[ MAX + 1];  
  58. int i= 0;  
  59. srand(5);  
  60. for( i = 1; i <= MAX; i++ )  
  61. data[i] = rand() % 500;  
  62.   
  63.   
  64. //建堆  
  65. for( i = 2; i <= MAX; i++ )  
  66. siftup(data,i);  
  67.   
  68.   
  69. //从后向前调整  
  70. for( i =MAX; i >= 2; i--)  
  71. {  
  72. swap(data, 1, i);  
  73. siftdown(data, i - 1 );  
  74. }  
  75.   
  76.   
  77. for( i = 1; i < MAX; i++ )  
  78. printf("%d\t", data[i]);  
  79.   
  80.   
  81. printf("\n");  
  82. return 0;  
  83. }  


堆排序与系统排序的效率比较:

  1. #include <stdio.h>  
  2. #include<iostream>  
  3. #include<string.h>  
  4. #include <algorithm>  
  5. #include <stdlib.h>  
  6. using namespace std;  
  7.   
  8. #define MAX 200000  
  9.   
  10. void swap( int *data, int i, int j)  
  11. {  
  12.     int temp = data[i];  
  13.     data[i] = data[j];  
  14.     data[j] = temp;  
  15. }  
  16.   
  17. //siftup比较好理解,将每一个元素都与自己的父亲比较,如果自己的值小于父亲的值,就互换,直到到堆顶,或父亲的值小于自己的值为止。  
  18. void siftup(int *data, int n )  
  19. {  
  20.     int i = n;  
  21.     int p;  
  22.     while( 1 )  
  23.     {  
  24.         if ( i == 1 ) break;  
  25.         p = i/2;  
  26.         if( data[p] <= data[i]) break;  
  27.         swap( data, i, p );  
  28.         i = p;  
  29.     }  
  30. }  
  31.   
  32. //这里的n其实意义不大,是指n之后的数据是符合要求的,n之前的数据可能不满足小根堆的要求,调整的方法也是从堆顶开始,初步向小调整  
  33. void siftdown( int *data, int n)  
  34. {  
  35.     int i = 1;  
  36.     int c = 0;  
  37.     while( 1 )  
  38.     {  
  39.         c = 2 * i;  
  40.         if( c > n ) break;  
  41.         //取两个孩子中较小的一个与自己作比较  
  42.         if( c + 1 <= n && data[ c + 1] < data[c] )  
  43.             c++;  
  44.   
  45.         //如果孩子的值小于自己的值,则互换  
  46.         if( data[i] <= data[c] )  
  47.             break;  
  48.         swap( data, c, i);  
  49.         i = c;  
  50.     }  
  51. }  
  52.   
  53. int main()  
  54. {  
  55.     double BegTime, EndTime;  
  56.     int data[ MAX + 1];  
  57.     int data2[ MAX + 1];  
  58.     data2[0] = 0;  
  59.     int i= 0;  
  60.     srand(5);  
  61.     for( i = 1; i <= MAX; i++ )  
  62.         data[i] = rand() % 500;  
  63.   
  64.     memcpy( data2, data, MAX+1);  
  65.     BegTime = clock();  
  66.     //建堆  
  67.     for( i = 2; i <= MAX; i++ )  
  68.         siftup(data,i);  
  69.   
  70.     //从后向前调整  
  71.     for( i =MAX; i >= 2; i--)  
  72.     {  
  73.         swap(data, 1, i);  
  74.         siftdown(data, i - 1 );  
  75.     }  
  76.     EndTime = clock();  
  77.     printf("HeapSort:%gms\n", (EndTime - BegTime) / 1000);  
  78.   
  79.     BegTime = clock();  
  80.     sort(data2, data2 + MAX + 1);  
  81.     EndTime = clock();  
  82.     printf("sort: %gms\n", (EndTime - BegTime) / 1000);  
  83.   
  84.   
  85.     printf("\n");  
  86.     return 0;  
  87. }  

测试结果如下:

HeapSort:60ms
sort: 40ms

结果表明,堆排序确实有着相当不错的表现,不到必须的时候,还是应当使用系统的sort函数,效率很高,且节约开发成本。

堆排序,布布扣,bubuko.com

堆排序

原文:http://blog.csdn.net/as365927660/article/details/24146859

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!