堆排序----c++实现

来源:互联网 发布:店铺效果图制作软件 编辑:程序博客网 时间:2024/05/21 09:36

改编自于:http://www.cppblog.com/kesalin/archive/2011/03/09/select_sort.html

堆排序

堆的定义:满足如下约束的 n 个关键字序列 Kl,K2,…,K称为堆,1 ≤ i ≤ n/2,
   (1) k≤ K2i 且 k≤ K2i+1 (小根堆)   或

(2) K≥ K2i 且 k≥ K2i+1 (大根堆)


从定义来看,堆实质上是满足如下性质的完全二叉树:树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在的话)结点的关键字。堆排序就是充分利用堆这种堆顶记录要么是最大的要么是最小的有序性质,每次在堆中选择最大或最小关键字完成排序。堆排序也是选择排序的一种,它比直接选择排序平均效率要高,因为直接选择排序,每次比较之后并没有对比较结果进行保存,导致可能存在重复的比较,而堆则利用其堆性质将比较结果保存在堆中了(非叶子节点的左边孩子一定不大于或不小于比右边的孩子)。


堆排序的关键就在于将待排序记录的建立成堆,建好堆之后,将无序区的堆顶记录(总是无序区的最大或最小)和无序区最后一个记录交换,交换之后,无序区的最后一个记录变成有序区的第一个记录,而无序区新的的堆顶记录不一定满足堆性质了,因而需要将无序区调整而堆。如此循环,直到无序区为空,结束排序。


添加的:如果建立一个递增的数组。则应该建立一个大顶堆,然后每次将a[0]与a[n-i]交换

build_heap(int* heap, int length)这个函数的作用就是指定起点:都是从len/2开始,往上走

adjust_heap(int* heap, int low, int high)这个函数的作用就是找出该堆中最大的元素,并且放在堆顶  
heap_sort(int* array, int length)这个函数的作用就是反复调用上一个函数。将堆顶元素与最低元素交换,别忘了每走一步堆底元素要减1呀~~~~
 还有一点疑问:
   for(i = length / 2; i >= 0; --i) {        adjust_heap(heap, i, length - 1);    }
对于下标从0开始的数组来说,i=length/2应该不对呀---例如lenght=11,则i=5;则比较不是结点5的子结点呀???????????



所以堆排序的主要步骤就分三步:

1,建立初始堆;-----找出该堆中最大元素

2,将无序的堆顶记录与最后一个记录交换,缩小无序区;

3,将交换之后的无序区调整为堆,重复步骤 2。


代码实现:

// 筛选法调整堆,除 [low] 之外,[low] 的两个孩子均已是大根堆void adjust_heap(int* heap, int low, int high){ assert(heap);#if 1 // 循环实现 int i = low; int j = 2 * i; int temp = heap[i]; while (j <= high) { // 若有两个孩子,j 为孩子中大的那个的下标 if (j < high && heap[j] < heap[j + 1]) { j = j + 1; } // 已是堆 if (temp >= heap[j]) { break; } // 继续筛选 else { heap[i] = heap[j]; i = j; j = 2 * i; } } heap[i] = temp;#else // 递归实现 int i = low; int j = 2 * i; int temp = heap[i]; if (j >= high) { return; } // 若有两个孩子,j 为孩子中大的那个的下标 if (j < high && heap[j + 1] > heap[j]) { j = j + 1; } // 已经为堆,无需调整 if (heap[low] >= heap[j]) { return; } heap[i] = heap[j]; heap[j] = temp; // 调整之后,[j, high] 可能不满足堆了,需继续调整 adjust_heap(heap, j, high);#endif}// 只有一个结点的树是堆,而在完全二叉树中,所有序号 i > n/2 的结点都是叶子,// 因此以这些结点为根的子树均已是堆。这样,我们只需依次将以序号为// n/2, n/2 - 1, …, 0 的结点作为根的子树都调整为堆即可。void build_heap(int* heap, int length){ assert(heap && length >= 0); int i; for(i = length / 2; i >= 0; --i) { adjust_heap(heap, i, length - 1); }}// 堆排序//void heap_sort(int* array, int length){ assert(array && length >= 0); if (length <= 1) { return; } int i, temp; // 将 [0, length - 1] 建成初始堆 build_heap(array, length); // 对当前无序区 [0, i - 1] 进行堆排序,共做 length - 1 趟。 for(i = length - 1; i > 0; --i) { // 将堆顶和堆中最后一个记录交换 temp = array[0]; array[0] = array[i]; array[i]= temp; // 将 [0, i - 1] 重新调整为堆,仅有 [0] 可能违反堆性质 adjust_heap(array, 0, i - 1); }}

时间复杂度分析:

 

堆排序的时间开销主要由建立初始堆和反复调整堆这两部分的时间开销构成,你可以想象成二叉树的情形。堆排序的平均时间复杂度为O(nlgn)。


空间复杂度分析:

很明显,为 0(1)。


补充:

堆排序是不稳定的就地排序。


=======================================================================================

测试:


运行结果:


=== 堆排序 ===

 original: 65 32 49 10 8 72 27 42 18 58 91

   sorted: 8 10 18 27 32 42 49 58 65 72 91


 original: 10 9 8 7 6 5 4 3 2 1 0

   sorted: 0 1 2 3 4 5 6 7 8 9 10


原创粉丝点击