堆的介绍
堆是一种特殊的完全二叉树(除叶结点外所有节点都有两个子节点)。
一共分为两种堆:大顶堆和小顶堆。
大顶堆:根节点大于所有的子节点,堆排序。
小顶堆:根节点小于所有的子节点,优先队列。
相关算法
下排
在O(lgN)的复杂度内,使某个父节点的值均大于其子节点的值,其子节点也满足此性质。
左节点索引计算公式为:父节点索引 * 2 + 1(数组索引从0开始)
右节点索引计算公式为:左节点索引 + 1(数组索引从0开始)
递归版本
void down(vector<int> &nums, int length, int k) {
int t = k, l = 2 * k + 1, r = l + 1;
// 判断左节点是否为最小值
if (l < length && nums[l] < nums[t]) t = l;
// 判断右节点是否为最小值
if (r < length && nums[r] < nums[t]) t = r;
if (k != t) {
swap(nums[k], nums[t]);
down(nums, length, t);
}
}
非递归版本
void down(vector<int> &nums, int length, int k) {
while (true) {
int t = k, l = k * 2 + 1, r = l + 1;
if (l < length && nums[l] > nums[t]) t = l;
if (r < length && nums[r] > nums[t]) t = r;
if (t != k) {
swap(nums[t], nums[k]);
k = t;
} else break;
}
}
堆化
对所给数组先进行堆化,只需对最后一层具有子节点的父节点进行down()操作即可,整个时间复杂度为O(N)。
最后一个具有子节点的父节点索引计算公式为:length / 2 - 1(数组索引从0开始)
for (int i = length / 2 - 1; i >= 0; --i) {
down(nums, length, i);
}
堆排序
#include <bits/stdc++.h>
using namespace std;
void down(vector<int> &nums, int length, int k) {
while (true) {
// 找到父节点和子节点中最大的节点,与父节点交换
int t = k, l = k * 2 + 1, r = l + 1;
if (l < length && nums[l] > nums[t]) t = l;
if (r < length && nums[r] > nums[t]) t = r;
if (t != k) {
swap(nums[t], nums[k]);
k = t;
} else break;
}
}
void heap_sort(vector<int> &nums) {
int length = nums.size();
for (int i = length / 2 - 1; i >= 0; --i) {
down(nums, length, i);
}
for (int i = length - 1; i > 0; --i) {
swap(nums[0], nums[i]);
down(nums, i, 0);
}
}
int main() {
vector<int> nums = {10, 20, 40, -1, -2, 7};
heap_sort(nums);
for (auto &n : nums) {
cout << n << endl;
}
return 0;
}