原理
核心思想:利用堆数据结构的特点
大根堆的特点
- 形状特征:完全二叉树(顺序存储的)
- 数值特征:对于每一个子树而言,根大于左孩子且大于右孩子
堆排序的步骤,n为数据规模
- 对n个数建大根堆
- 把堆顶放在最后一个位置
- 减少数据规模,再对n-1个数建大根堆
- 重复2和3,直到数据规模为1时
具体排序步骤: 对N个数建立大根堆
- 从最后一个父节点(编号为N/2,下标为N/2-1)开始建堆,直到堆顶(编号为1,下标为0)
- 先父节点下的兄弟节点比较(选出大的)
- 再父子比较,如果父比子小就交换并向下循环,如果父节点大退出循环
- 堆顶放最后,缩减堆的规模
- 循环建堆(从堆顶开始,建完后堆顶放最后,缩减堆的规模,直到堆的规模为1)
性能
时间复杂度:O(n * logn)【建堆需要O(n),调整堆需要O(n * logn)】
空间复杂度:O(1)
代码
输入:数组地址,数组长度
输出:升序排列的数组
heap_sort.c
//如果换成字符串类型的数组,那么除了修改元素类型外,涉及到比较操作和交换操作的地方,全部都要改成字符串形式的
//如果不是字符串类型的,只需修改类型即可
void adjust_maxheap(int *arr, int pos, int len)
{
//根据父节点的下标,求出子节点的下标
int dad = pos;
int son = 2 * pos + 1;
while (son < len) {
if (son + 1 < len && arr[son + 1] > arr[son]) {
//如果右孩子存在,且大于左孩子,更新孩子节点
son = son + 1;
}
if (arr[son] > arr[dad]) {
//如果子节点大于父节点,交换父子节点
int tmp = arr[son];
arr[son] = arr[dad];
arr[dad] = tmp;
//更新父子节点,向下循环,调整堆
dad = son;
son = 2 * dad + 1;
}
else {
//父节点大于子节点,符合大根堆,退出
break;
}
}
}
void heap_sort(int *arr, int len)
{
//堆的规模小于2,不用排序
if (2 > len) {
return;
}
//从最后一个父节点开始,建立大根堆,直到首节点
for (int i = len / 2 - 1; i >= 0; --i) {
adjust_maxheap(arr, i, len);
}
//堆顶放最后
int tmp = arr[0];
arr[0] = arr[len - 1];
arr[len - 1] = tmp;
//不断缩减堆的规模,重建堆,找出当前规模的最大值
for (int size = len - 1; size >= 1; --size) {
adjust_maxheap(arr, 0, size);
//建好大根堆后,堆顶放最后
int tmp = arr[0];
arr[0] = arr[size - 1];
arr[size - 1] = tmp;
}
}