您的位置:首页 > 其它

算法熟记-排序系列-堆排序

2011-06-08 21:16 453 查看
1. 简述

假设待排序数组为 int array[], 数组长度为n。
主要是利用堆的性质。对于升序排序,使用最大堆。
首先,建堆,使用递归后根序遍历得方法,通过交换元素,保证根元素比孩子元素大。
第1趟,堆顶元素array[0]与array[n-1]交换,保证array[n-1]的数值正确,根据array[0]新的数值更新堆。
第2趟,堆顶元素array[0]与array[n-2]交换,保证array[n-2]的数值正确,根据array[0]新的数值更新堆。
···

第n-1趟,堆顶元素array[0]与array[1]交换,保证array[1]的数值正确,根据array[0]新的数值更新堆。

2. 复杂度

平均时间复杂度为O(N*logN),空间复杂度为O(1)。

3. 代码

void make_heap(int array[], int n, int node) { // 自底向上,构建堆
int left = 2 * node + 1;
int right = 2 * node + 2;
if(left > n-1) return;
else if(right > n-1) { // 堆是完全的二叉树,所以此时不需要递归
if(array[node] < array[left]) {
swap(array[node], array[left]);
}
}
else {
make_heap(array, n, left);
make_heap(array, n, right);
if(array[node] < array[left] && array[right] <= array[left]) {
swap(array[node], array[left]);
}
else if(array[node] < array[right] && array[left] <= array[right]) {
swap(array[node], array[right]);
}
}
}
void update_heap(int array[], int n, int node) { // 自顶向下,更新堆
int left = 2 * n + 1;
int right = 2 * n + 2;
if(left > n-1) return;
else if(right > n-1) {
if(array[node] < array[left])
swap(array[node], array[left]);
}
else {
if(array[node] < array[left] && array[right] <= array[left]) {
swap(array[node], array[left]);
update_heap(array, n, left);
}
else if(array[node] < array[right] && array[left] <= array[right]) {
swap(array[node], array[right]);
update_heap(array, n, right);
}
}
}
void heap_sort(int array[], int n) {
make_heap(array, n, 0);
for(int i=n; i>=1; i--) {
swap(array[i], array[0]);
update_heap(array, n, node);
}
}
实际上,堆的构建和更新都可以使用非递归的方式实现,对于堆的构建,需要首先找到最后一个有孩子的节点array[k],然后从array[k]一直更新到array[0]即可,其中的k=n/2。k的求法如下:假设k存在,2*k+1=n或者2*k+2=n,对于第一种情况,k==n/2,对于第二种情况,k==n/2-1。对于堆的更新,就更简单了,只要从array[0]开始,选择一条通路,一直向下更新,直到没有孩子了为止。
值得注意的是,对于下标从0开始的数组,k号节点的孩子节点分别是2*k+1和2*k+2。 而对于下标从1开始得数组,k号节点的孩子节点分别是2*k和2*k+1。
堆排序属于选择排序,实际上就是利用最大堆这个数据结构,每次选择一个剩余元素中最大的元素,交换到合适的位置上去。

4. 参考资料

维基百科-堆排序 http://en.wikipedia.org/wiki/Heapsort
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: