简述
堆是具有以下性质的完全二叉树:每个节点的值都大于或等于其左右孩子的节点的值,称为大顶堆;每个节点的值都小于或等于其左右孩子节点的值,成为小顶堆。
堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。
代码
#include <QDebug>
#include <QApplication>
void mDebug(int a[], int len)
{
QString str;
for (int i = 0; i < len; i++)
{
str += QString("%1 ").arg(a[i]);
}
qDebug() << str;
}
void swap(int &a, int &b)
{
int t = a;
a = b;
b = t;
}
// a: 数组, i:节点, len:数组长度
void adjustHeap(int a[], int i, int len)
{
// 首先把节点数值记住,只需要在最后做一次交换就行
int temp = a[i];
// 2*i+1: 节点的左值, 2*i+2: 节点的右值
// 这里按照最大堆排序,轮询完全二叉树,把各个小分叉的三个数值中最大的放父节点位置
for (int j = 2*i+1; j < len; j = 2*i+1)
{
// 比较左右值,找到最大的一个
if (j+1 < len && a[j] < a[j+1])
{
j++;
}
// 在找到左右值最大的一个后,再与父节点最原始数值比较,如果最大值比父节点还大,
// 那就先把最大值赋值给父节点,然后再把父节点索引更新为最大值所在索引
if (a[j] > temp)
{
a[i] = a[j];
// 因为索引j的值与父节点i发生了改变,所以需要把j当作是父节点,
// 然后再重新遍历j节点,以保证j节点是最大的数值
i = j;
}
// 如果都没有发生改变就直接退出循环
else
{
break;
}
}
// 最后得到的i节点必然是已经做过交换的节点,只需要把temp重新赋值上去就完成了交换
a[i] = temp;
}
// a: 数组, len:数组长度
void HeapSort(int a[],int len)
{
// len/2-1: 数组最后一个非叶子节点
// 构建大顶堆, 从组后一个非叶子节点,从下往上从左往右调整
for (int i = len/2-1; i >= 0; i--)
{
adjustHeap(a, i, len);
}
// 调整堆结构+交换堆顶元素与末尾上元素
for (int i = len - 1; i > 0; i--)
{
// 经过调整,每次a[0]元素必然是最大的,
// 把它与最后一个元素交换,然后剔除掉最后一个元素再重新调整
swap(a[0], a[i]);
// 因为已经建立了最大堆,而原本a[0]是最大的元素,
// 可是经过交换后a[0]的值就不是最大的了,
// a[0]发生了改变所以就需要从节点0开始往下调整
adjustHeap(a, 0, i);
}
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
int s[] = {50, -1, 3, 4, 0, 5, 1000, 6, 7, 12, 8, 1, 2, 9, -530, 450, 77} ;
int len = sizeof(s)/sizeof(s[0]);
// 堆排序
HeapSort(s, len);
// 打印
mDebug(s, len);
return a.exec();
}
输出
"-530 -1 0 1 2 3 4 5 6 7 8 9 12 50 77 450 1000 "