这是我参与「第三届青训营 -后端场」笔记创作活动的的第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);
}
}