堆排序( Heap Sort )只需要 一 个记录大小的辅助空间,每个待排序的记录仅占有一个
存储空间。
堆的定义如下: n 个元素的序列 { k1, k2 ,..., kn }当且仅当满足下关系时,称之为堆。

若将和此序列对应的一维数组(即以一维数组作此序列的存储结构)看成是一个完全二叉树,则堆的含义表明,完全二叉树中所有非终端结点的值均不大于(或不小于)其左、右孩子结点的值。由此,若序列 { k1, k2 ,..., kn } 是堆,则堆顶元素(或完全二叉树的根)必为序列中 n 个元素的最小值(或最大值)。
若在输出堆顶的最小值之后,使得剩余 n - 1 个元素的序列重又建成一个堆,则得到n 个元素中的次小值。如此反复执行,便能得到一个有序序列,这个过程称之为堆排序。
源码如下:
#include <iostream>
#define SPACE 0x100
// 检测左边的数是否小于右边的,如果是,结果为 1 ,否则 0。
#define LT(left, right) (left < right ? 1 : 0)
// 检测左边的数是否大于右边的,如果是,结果为 1 ,否则 0。
#define GT(left, right) (left > right ? 1 : 0)
//#define DESCENDING_ORDER //去掉注释,则使用降序排列
using namespace std;
struct Data
{
int key;
};
struct SqList
{
Data r[SPACE];
int length;
};
typedef SqList HeapType; // 堆采用顺序表存储表示
// 输出数组
void print(HeapType &L, int low, int high)
{
cout << "[ ";
for (int i = low; i <= high; i++)
{
cout << L.r[i].key << ", ";
}
cout << "]" << endl;
}
// 已知 H.r[s .. m]中记录的关键字除 H. r[s].key 之外均满足堆的定义,本函数调整 H.r[s]
// 的关键字,使 H.r[s .. m]成为一个大顶堆(对其中记录的关键字而言)
void HeapAdjust(HeapType &H, int s, int m)
{
Data rc = H.r[s];
//沿 key 较大的孩子结点向下筛选
for (int j = 2 * s; j <= m; j *= 2)
{
// j 为 key 较大的记录的下标
#ifndef DESCENDING_ORDER
if (j < m && LT(H.r[j].key, H.r[j + 1].key))
{
++j;
}
#else
if (j < m && GT(H.r[j].key, H.r[j + 1].key))
{
++j;
}
#endif
// rc 应插入在位置 s 上
#ifndef DESCENDING_ORDER
if (!LT(rc.key, H.r[j].key))
{
break;
}
#else
if (!GT(rc.key, H.r[j].key))
{
break;
}
#endif
H.r[s] = H.r[j];
s = j;
}
H.r[s] = rc; // 插入
}
// 对顺序表 H 进行堆排序。
void HeapSort(HeapType &H)
{
// 这里建立初始堆
// 默认把 H.r[1 ... H.length]建成 大顶堆
// 如果定义了 DESCENDING_ORDER 则建成 小顶堆
for (int i = H.length / 2; i > 0; --i)
{
HeapAdjust(H, i, H.length);
}
cout << "初始堆 :";
print(H, 1, H.length);
for (int i = H.length; i > 1; --i)
{
// 将堆顶记录和当前未经排序子序列 Hr[l ... i]中最后一个记录相互交换
Data temp = H.r[1];
H.r[1] = H.r[i];
H.r[i] = temp;
// 将 H.r[ l ... i-1 ]重新调整为 大顶堆
// 如果定义了 DESCENDING_ORDER 则重新调整为 小顶堆
HeapAdjust(H, 1, i - 1);
}
}
int main()
{
// 使用的书中的数据,可以参照书本进行调试
HeapType heap = {0, 49, 38, 65, 97, 76, 13, 27, 49};
heap.length = 8;
cout << "初始数据:";
print(heap, 1, heap.length);
HeapSort(heap);
cout << "结果数据:";
print(heap, 1, heap.length);
return 0;
}
运行结果:
初始数据:[ 49, 38, 65, 97, 76, 13, 27, 49, ]
初始堆 :[ 97, 76, 65, 49, 49, 13, 27, 38, ]
结果数据:[ 13, 27, 38, 49, 49, 65, 76, 97, ]
堆排序的关键字比较次数与元素初始排列次序是有关系的
因为进行堆排序的时候,如果第一次判断失败了,会直接返回,但是如果成功了的话,则会继续和他的子树进行判断,而元素初始排列次序会影响第一次判读的对错,使得堆排序的关键字比较次数与元素初始排列次序是有关。
资料来源:《数据结构》严蔚敏版