堆排序算法的实现 | 青训营笔记

224 阅读2分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记

了解到golang1.19将引入更快的排序算法,同时知道了该排序算法结合了插入排序,快速排序和堆排序。

堆排序因为其稳定的时间复杂度,成为了整个排序算法的兜底所在而不可或缺。我也学习了这一算法并结合自己的理解写下了笔记。

笔记内容写在代码注释中,整体的代码阅读大体上为线性阅读,可以更加流畅的了解堆排序是怎么实现的,基本上不会出现需要找某个函数出现在哪的情况。

具体实现如下:

#include<iostream>
using namespace std;
void HeapSort(int n);
void BuildHeap(int n);
void Heapify(int x, int n);
int a[15];
int main() {
    //用随机数代替输入
    srand(time(0));
    int n = 10;
    for (int i = 1; i <= n; i++)a[i] = rand() % 101 - 50;
    //排序前输出
    cout << "排序前:" << endl;
    for (int i = 1; i <= n; i++)cout << a[i] << " ";
    cout << endl;
    //堆排序--第24行
    HeapSort(n);
    //排序后输出
    cout << "排序后:" << endl;
    for (int i = 1; i <= n; i++)cout << a[i] << " ";
    cout << endl;
    return 0;
}
//堆排序
void HeapSort(int n) {
    //传入参数为n,即输入数据的下标为1~n,可以将这个数组视为二叉树,根节点为1,节点x的左节点为x*2,右节点为x*2+1。
    //首先用BuildHeap()函数将这棵二叉树转化为最大堆,那么根据定义,最大值出现在根节点中。
    BuildHeap(n);//BuildHeap--第36行
    for (int i = n; i >= 2; i--) {
        //i表示当前堆的大小,而1是最大值所在的位置。交换两者,并将堆的大小-1,那么最大值就被移动到了a[i]
        //当所有操作结束后,最大值就会以此从右到左排列,即最小值从左到右排列
        swap(a[i], a[1]);
        Heapify(1, i - 1);
    }
}
//创建最大堆
void BuildHeap(int n) {
    //最大堆需要满足的条件是,任意节点的数值大于等于其子节点的数值,即a[x]>=a[x*2]&&a[x]>=a[x*2+1]
    //所以我们可以从子节点开始逐步用Heapify()函数来满足这一条件
    //不难发现i>n/2时,其左节点>n,超出了二叉树的范围。
    for (int i = n / 2; i >= 1; i--) {
        Heapify(i, n);//堆化--第45行
    }
}
//堆化(专有名词)
//这里堆化的目的是使整个二叉树变为最大堆
//实际操作为
//1:如果该节点大于等于其左右节点,不做操作
//2:否则则与其最大的子节点交换位置,同时递归操作改子节点的位置
void Heapify(int x, int n) {
    if (x >= n) {
        return;
    }
    //mxp表示节点、左节点、右节点三个节点中最大值所在的位置
    int mxp = x;
    //左右节点下标
    int le = x * 2, ri = x * 2 + 1;
    //如果le小于等于堆的大小n同时a[le]的值大于当前最大值,记录最大值位置
    if (le <= n && a[le] > a[mxp]) {
        mxp = le;
    }
    //与le同理
    if (ri <= n && a[ri] > a[mxp]) {
        mxp = ri;
    }
    //如果最大值位置不是节点本身,则说明存在比自身大的子节点,交换数值并进行下一步操作
    if (mxp != x) {
        swap(a[x], a[mxp]);
        Heapify(mxp, n);
    }
}