二叉堆是一颗完全二叉树,二叉堆中某个节点的值总是不大于(小于)其父节点的值,称为最大堆(最小堆),该实现从数组下标为1开始存储元素,0位置预留不做处理,当然,完全可以从0开始存储元素,两者之间的差别就是求取某个节点的父节点、左右子节点以及最后一个非叶子节点的公式有所区别。
最大堆实现:
// 利用数组存储实现堆,且从数组下标1位置开始存储元素,0位置不存储有效元素
// parent(i) = i/2
// leftChild(i) = 2 * i
// rightChild(i) = 2 * i + 1
// 最后一个非叶子节点索引为 count / 2, 其中 count 为当前堆中元素个数
template<typename T>
class MaxHeap_FixedSpace {
using uint = unsigned int;
public:
MaxHeap_FixedSpace(const T arr[], int n) {
data = new T[n + 1];
memcpy(data + 1, arr, n * sizeof(T));
count = n;
// 构建最大堆
for (int i = count / 2; i > 0; --i) {
shiftDown(i);
}
}
~MaxHeap_FixedSpace() { delete[] data; }
T popMax() {
if (count > 0) {
T res = data[1];
std::swap(data[1], data[count]);
--count; // 特别注意,在重新shiftDown之前,必须先减少元素,确保被移除
shiftDown(1);
return res;
}
std::cerr << "try to pop from empty heap" << std::endl;
return T();
}
void debugPrint() {
for (int i = 0; i < count; ++i) {
std::cout << data[i + 1] << " ";
}
std::cout << std::endl;
}
private:
void shiftDown(uint i) {
uint k = i;
while (2 * k <= count) {
uint j = 2 * k;
if ((j + 1 <= count) && (data[j] < data[j + 1])) {
// 存在右子节点,且右子节点元素大于左子节点元素
++j;
}
if (data[k] < data[j]) {
// 父节点元素小于左右子节点元素最大者,则交换父子节点(保持最大堆性质),继续对该子节点进行shiftDown操作
std::swap(data[k], data[j]);
k = j;
continue;
}
break;
}
}
private:
uint count = 0;
T *data;
};
堆排序,利用该最大堆进行排序,注意,这里的堆排序利用了额外的空间,即堆得空间
void HeapSort_WithMaxHeapFixed(int arr[], int n) {
HeapSpace::MaxHeap_FixedSpace<int> maxHeap_FixedSpace(arr, n); // 用待排序数组构造最大堆
for (int i = 0; i < n; ++i) {
arr[n - i - 1] = maxHeap_FixedSpace.popMax(); // 依次取出堆得最大元素用于排序
}
}
测试
void MaxHeapFixedTest() {
int arr1[]{9, 3, 7, 6, 5, 1, 8, 2, 4};
int arr1r[]{1, 2, 3, 4, 5, 6, 7, 8, 9};
HeapSort_WithMaxHeapFixed(arr1, 9);
assert(SortCommon::ArrEqual(arr1, arr1r, 9) == true);
int arr2[]{1};
int arr2r[]{1};
HeapSort_WithMaxHeapFixed(arr2, 1);
assert(SortCommon::ArrEqual(arr2, arr2r, 1));
int arr3[]{2, 1};
int arr3r[]{1, 2};
HeapSort_WithMaxHeapFixed(arr3, 2);
assert(SortCommon::ArrEqual(arr3, arr3r, 2));
int arr4[]{5, 3, 3, 7, 5, 5, 5, 5, 1, 7, 2, 7, 7, 7, 7};
int arr4r[]{1, 2, 3, 3, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7};
HeapSort_WithMaxHeapFixed(arr4, 15);
assert(SortCommon::ArrEqual(arr4, arr4r, 15));
int arr5[]{1, 3, 2};
int arr5r[]{1, 2, 3};
HeapSort_WithMaxHeapFixed(arr5, 3);
assert(SortCommon::ArrEqual(arr5, arr5r, 3));
return;
}