算法简介
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。通常堆是通过一维数组来实现的。在数组起始位置为0的情形中:
父节点i的左子节点在位置:2i+1
父节点i的右子节点在位置:2i+2
子节点i的父节点在位置:(i-1)/2
算法思想
首先将待排序的数组构造成一个大根堆,此时,整个数组的最大值就是堆结构的顶端。将顶端的数与末尾的数交换,此时,末尾的数值为最大值,剩余待排序数组个数为n-1。将剩余除末尾外的n-1个数再构造成大根堆,再将顶端数与n-1位置的数交换,如此反复执行,便能得到有序数组。
堆操作
最大堆调整(Max_Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点
创建最大堆(Build_Max_Heap):将堆所有数据重新排序
堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算
代码实现
最大堆调整
void Heapify(int *num, int len, int k)
{
if (k < len)
{
int root = k; // 根结点
int lchild = 2*k + 1; // 左孩子结点
int rchild = 2*k + 2; // 右孩子结点
// 查找左右孩子结点中的最大结点
if (lchild < len && num[root] < num[lchild])
{
root = lchild;
}
if (rchild < len && num[root] < num[rchild])
{
root = rchild;
}
// 交换最大结点到根结点
if (root != k)
{
num[root] ^= num[k];
num[k] ^= num[root];
num[root] ^= num[k];
// 对交换后的孩子结点子树进行最大堆调整
Heapify(num, len, root);
}
}
}
堆排序
void CreateHeap(int *num, int len)
{
int i;// 最后一个结点下标
int last = len - 1;// 最后一个结点的父结点下标
int father = (last - 1) / 2;
// 从最后一个结点的父结点到根结点,依次进行最大堆调整
for (i = father; i >= 0; i--)
{
Heapify(num, len, i);
}
}
void HeapSort(int *num, int len)
{
CreateHeap(num, len);
int i;
for (i = len - 1; i >= 0; i--)
{
// 将最大堆的根结点换到最后一个结点
num[i] ^= num[0];
num[0] ^= num[i];
num[i] ^= num[0];
Heapify(num, i, 0);//对根结点做最大堆调整,只考虑最大孩子的分支(末尾结点不调整)
}
}
主函数部分调用函数
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define size 10
int main(int argc, char *argv[])
{
int i;
int num[size] = {8, 21, 13, 16, 7, 33, 21, 10, 1, 5};
printf("调整前:");
for (i = 0; i < size; i++)
{
printf("%d ", num[i]);
}
printf("\n");
HeapSort(num, size);
printf("调整后:");
for (i = 0; i < size; i++)
{
printf("%d ", num[i]);
}
printf("\n");
return 0;
}
排序结果
YX@ubuntu:~/2207/sj$ ./a.out
调整前:8 21 13 16 7 33 21 10 1 5
调整后:0 5 7 8 10 13 16 21 21 33
时间复杂度
平均时间复杂度:O(nlogn),不稳定。