数据结构 堆

228 阅读2分钟

堆的介绍

堆是一种特殊的完全二叉树(除叶结点外所有节点都有两个子节点)。

一共分为两种堆:大顶堆小顶堆

大顶堆:根节点大于所有的子节点,堆排序。

小顶堆:根节点小于所有的子节点,优先队列。

相关算法

下排

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;
}