利用最大堆实现。
最大堆:最大堆性质是除了根结点意外的所有结点 i 都要满足A[parent[i]] >= A[i]
需要利用到的一个性质:当用数组表示存储n个元素的堆时,叶结点的下标分别是n/2, n/2+1, n/2 + 2, ……,n – 1. (下标从0开始)
需要用到的函数有:
void max_heapify(int *a, int i) //通过让a[i]的值在最大堆中“逐级下降”,从而使得以下标i为根结点的字数重新遵循最大堆的性质。O(lgn)
void build_max_heap(int *a) //对树中非叶结点都调用一次max_heapify(a, i)。 O(n)
void heapsort(int *a, int n) //对长度为n的数组a调用堆排序。 O(nlgn)
//heapsort.cpp
inline void swap(int &a, int &b) { int t = a; a = b; b = t; }
inline int parent(int i) { return (i-1) >> 1; } //下标都是从0开始,与算导上不一样
inline int left(int i) { return (i << 1) + 1; }
inline int right(int i) { return (i << 1) + 2; }
int heap_size, heap_length; //heap_length是数组元素个数,heap_size是有多少个元素存储在数组中。0<=heap_size<=heap_length
void max_heapify(int *a, int i) { //O(lgn), 维护heap的性质,使得以下标i为根结点的子树重新遵循最大堆的性质
int l = left(i), r = right(i);
int largest = 0;
if (l < heap_size && a[l] > a[i])
largest = l;
else
largest = i;
if (r < heap_size && a[r] > a[largest])
largest = r;
if (largest != i) {
swap(a[i], a[largest]);
max_heapify(a, largest);
}
}
void build_max_heap(int *a) { //O(n), 对树中非叶结点都调用一次 max_heapify
heap_size = heap_length;
for (int i = heap_length/2 - 1; i >= 0; --i) //可以证明下标为n/2-1到0的结点为非叶结点
max_heapify(a, i); //i逆序递减的原因:任何时候对结点i调用max_heapify,该i的两个子树都是最大堆
}
void heapsort(int *a, int n) { //O(nlgn),调用heapsort(a, n)对数组a排序
heap_length = n;
build_max_heap(a);
for (int i = heap_length - 1; i >= 1; --i) {
swap(a[0], a[i]);
--heap_size;
max_heapify(a, 0);
}
}