原理
以从小到大排序为示例,我们借助大根堆。
- 从第一个非叶子节点开始,把二叉树调整成一个大根堆, 即从第
(n-1) / 2
号元素开始到堆顶元素(0号位元素),进行下沉操作。 - 把堆顶元素和末尾元素进行交换, 此时我们将值最大的元素放到了最后。 所以当前状态打破了大根堆节点之间的大小关系,因此需要从0号位元素继续开始堆的下沉调整,下沉调整的时候我们就不需要考虑最后一个元素了,因为此时最后一个元素是最大值。如此循环,每次堆顶都是最大的元素。
时间空间复杂度
排序算法 | 平均时间复杂度 | 最好时间复杂度 | 最坏时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
堆排序 | O() | O() | O() | O(1) | 不稳定 |
代码
#include <iostream>
#include <stdlib.h>
#include <time.h>
using namespace std;
// 堆的下沉调整
void siftDown(int arr[], int i, int size) {
int val = arr[i]; // val 为要调整的值
while (i < size / 2) // 注意此处的比较
{
int child = 2 * i + 1; // 左孩子
if (child + 1 < size && arr[child + 1] > arr[child]) { // 右孩子存在,并且右孩子大于左孩子
child = child + 1; // 记录数值大的孩子的位置
}
if (arr[child] > val) {
arr[i] = arr[child];
i = child;
} else {
break;
}
}
arr[i] = val;
}
// 堆排序
void HeapSort(int arr[], int size) {
int n = size - 1; // 末尾元素的下标
// 从第一个非叶子节点,调整为大根堆
for (int i = (n - 1) / 2; i >= 0; i--) {
siftDown(arr, i, size);
}
// 把堆顶元素和末尾元素进行交换, 从堆顶开始进行下沉操作
for (int i = n; i > 0; i--) {
int tmp = arr[0];
arr[0] = arr[i];
arr[i] = tmp;
siftDown(arr, 0, i); // 第三个参数表示参与调整的元素个数
}
}
int main() {
int arr[10];
srand(time(nullptr));
for (int i = 0; i < 10; i++) {
arr[i] = rand() % 100 + 1;
}
for (int v : arr) {
cout << v << " ";
}
cout << endl;
HeapSort(arr, 10);
for (int v : arr) {
cout << v << " ";
}
cout << endl;
return 0;
}
测试
➜ build git:(main) ✗ ./HeapSort
91 93 13 98 25 46 49 90 82 65
13 25 46 49 65 82 90 91 93 98