声明:引用请注明出处http://blog.csdn.net/lg1259156776/
在我的博文《“主宰世界”的10种算法短评》中给出的首个算法就是高效的排序算法。本文将对排序算法做一个全面的梳理,从最简单的“冒泡”到高效的堆排序等。
上博文讲述了选择排序中的简单排序算法,本文介绍的堆排序是树性选择排序,采用堆这个数据结构来辅助排序。
如果在对象序列中有两个对象
算法执行时所需的附加存储。
关于堆这一数据结构的阐释可以参看我的博文《数据结构(三):非线性逻辑结构-特殊的二叉树结构:堆、哈夫曼树、二叉搜索树、平衡二叉搜索树、红黑树、线索二叉树》。为了保证完整性,这里简要介绍一下。
堆(heap order)是一种特殊的表,如果将它看做是一颗完全二叉树的层次序列,那么它具有如下的性质:每个节点的值都不大于其孩子的值,或每个节点的值都不小于其孩子的值,前者为小根堆,后者为大根堆。
堆排序(HeapSort)是一树形选择排序。堆排序的特点是:在排序过程中,将
直接选择排序中(可以参看《排序算法六:选择排序之简单选择排序》),为了从
#include <iostream>
using namespace std;
void siftDown( int *a, int k, int N);
void swap(int *m, int *n)
{
int tmp;
tmp = *m;
*m = *n;
*n = tmp;
}
void heapsort( int a[], int N){
/* heapify */
for (int k = N/2; k >= 0; k--) {
siftDown( a, k, N);
}
while (N-1 > 0) {
/* swap the root(maximum value) of the heap
with the last element of the heap */
swap(a[N-1], a[0]);
/* put the heap back in max-heap order */
siftDown(a, 0, N-1);
/* N-- : decrease the size of the heap by one
so that the previous max value will
stay in its proper placement */
N--;
}
}
void siftDown( int *a, int k, int N){
while ( k*2 + 1 < N ) {
/* For zero-based arrays, the children are 2*i+1 and 2*i+2 */
int child = 2*k + 1;
/* get bigger child if there are two children */
if ((child + 1 < N) && (a[child] < a[child+1])) child++;
if (a[k] < a[child]) { /* out of max-heap order */
swap( a[child], a[k] );
/* repeat to continue sifting down the child now */
k = child;
}
else
return;
}
}
int main()
{
int i;
int a[] = {19, 17, 16, 12, 9, 15, 1, 2, 11, 7, 3, 10, 14};
const size_t sz = sizeof(a)/sizeof(a[0]);
for (i = 0; i < sz; i++)
cout << a[i] << " ";
cout << endl;
cout << "----------------------------------" << endl;
heapsort(a, sz);
for (i = 0; i < sz; i++)
cout << a[i] << " ";
cout << endl;
return 0;
}
输出为:
19 17 16 12 9 15 1 2 11 7 3 10 14
----------------------------------
1 2 3 7 9 10 11 12 14 15 16 17 19
首先利用siftdown将测试数据建堆,然后将堆顶的数据跟尾部数据交换,并重新调整堆和减少堆中数据的数量。而shiftdown实现的是将堆中某个输入的数据节点处的整个二叉树枝的堆的调整(默认只有该位置与其左右孩子可能需要调整,其左右孩子的二叉树枝是满足堆结构的)。因此再利用shiftdown建堆的时候,得先从最右下边的父节点开始调整,因为它调整完毕后,就满足堆结构要求,之后基于它的调整才能使用siftdown来完成。
堆排序的时间复杂度是O(nlgN),与快速排序达到相同的时间复杂度。但是在实际应用中,我们往往采用快速排序而不是堆排序。这是因为快速排序的一个好的实现,往往比堆排序具有更好的表现。堆排序的主要用途,是在形成和处理优先级队列方面。另外,如果计算要求是类优先级队列(比如,只要返回最大或者最小元素,只有有限的插入要求等),堆同样是很适合的数据结构。
堆排序方法对记录数较少的文件并不值得提倡,但对n较大的文件还是很有效的。因为其运行时间主要耗费在建初始堆和调整建新堆时进行的反复“筛选”上。
堆排序在最坏的情况下,其时间复杂度也为O(nlogn)。相对于快速排序来说,这是堆排序的最大优点。此外,堆排序仅需一个记录大小的供交换用的辅助存储空间。
堆排序并不是一个稳定的排序。主要是借助了堆这个数据结构,一旦调整好堆的结构,就能保证root对应的是最大的项,在实现中随着尾部的数据排序,逐渐减小堆中数据的数量,并不断的将顶部的数排在尾部已经排好序的数据前面,就这样当堆数量减少到1的时候排序也就完成了。
2015-9-26 艺少
版权声明:本文为博主原创文章,转载请注明出处http://blog.csdn.net/lg1259156776/。
原文地址:http://blog.csdn.net/lg1259156776/article/details/48750471