堆排序(heapsort)是一种原地排序,任何时候数组中只有常数个元素存储在输入数组以外,运行时间复杂度O( n log(n) )。
堆数据结构是一种数组对象,可视为一棵完全二叉树,树中每个节点与数组中存放该节点值的那个元素对应,除最后一层外,树的每一层都是填满的。
堆排序过程涉及以下相关操作:计算某个节点的父节点,计算某个节点的左子节点,计算某个节点的右子节点,维护堆的性质,建堆,堆排序。
维护最大堆性质:当某个节点的值小于其左或右子节点时,违反了最大堆性质时,让该节点的值在最大堆的树中”下降“,比较该节点与左右子节点,让其与最大的子节点的值交换,然后递归调用维护最大堆性质的函数维护该节点的最大子节点的最大堆性质直到堆的数据结构不再有任何变化。
建堆:在堆的树结构中,下标为(n/2+1)~n中的元素都是树中的叶子节点,都可看成只有一个元素的堆,然后对其他节点自大至小都调用一遍维护最大堆性质函数即可完成建堆过程。
堆排序:首先进行建堆,然后每次交换堆中最后一个元素与堆的根元素,最后维护根元素的最大堆性质,直到堆中只剩一个元素即完成完成排序过程
具体代码实现如下:
#include <assert.h>
#include <stdio.h>
#include <iostream>
//计算父节点
int Parent(const int i){
assert( i >= 0 );
return (i-1)/2;
}
//计算左子节点
int Left(const int i){
assert( i >= 0 );
return 2*i+1;
}
//计算右子节点
int Right(const int i){
assert( i >= 0 );
return 2*i+2;
}
//维护最大堆性质
void MaxHeapify(int* data, const int heap_size, const int i){
assert( data != NULL );
assert( i >= 0 );
assert( i < heap_size );
int left = Left( i );
int right = Right( i );
int largest = 0;
if( (left < heap_size) && (data[ left ] > data[ i ] ) ){
largest = left;
}
else{
largest = i;
}
if( (right < heap_size) && (data[ right ] > data[ largest ]) ){
largest = right;
}
if( largest != i){
int temp = data[ i ];
data[ i ] = data[ largest ];
data[ largest ] = temp;
MaxHeapify( data, heap_size, largest );
}
}
//建堆
void BuildMaxHeap(int *data, const int length){
assert( data != NULL );
assert( length > 0 );
int heap_size = length;
for( int i = (length/2 - 1); i >=0; --i){
MaxHeapify(data, heap_size, i);
}
}
//堆排序
void HeapSort(int *data, const int length)
{
assert( data != NULL );
assert( length > 0 );
int heap_size = length;
BuildMaxHeap(data, length);
for( int i = length - 1; i > 0; --i){
int temp = data[ 0 ];
data[ 0 ] = data[ i ];
data[ i ] = temp;
heap_size--;
MaxHeapify(data, heap_size, 0);
}
}
int main()
{
int data[]={4,1,3,2,16,9,10,14,8,7};
std::cout<<"Before: ";
for(auto d : data){
std::cout<<d<<" ";
}
std::cout<<std::endl;
Heap::HeapSort(data, 10);
std::cout<<"After: ";
for(auto d : data){
std::cout<<d<<" ";
}
std::cout<<std::endl;
return 0;
}