【排序】堆排序算法

堆排序

1.代码模板

void swap(SqList *L, int i, int j)
{
    int temp = L->arr[i];
    L->arr[i] = L->arr[j];
    L->arr[j] = temp;
}
void HeapSort(SqList *L);  // 堆排序
void HeapAdjust(SqList *L, int s, int m);   // 堆调整 将L->arr[s...m]调整成一个大顶堆

void HeapSort(SqList *L)
{
    // 从下到上,从右到左,把L中的arr构建成一个大顶堆
    for (int i = L->length / 2; i > 0; i -- )  
        HeapAdjust(L, i, L->length);
    // 正式的排序过程
    for (int i = L->length; i > 1; i -- ) {
        swap(L, 1, i);  // 将堆顶元素和当前未经排序子序列的最后一个元素交换
        HeapAdjust(L, 1, i - 1);  // 将L->arr[1...i-1]重新调整为大顶堆
    }
}

void HeapAdjust(SqList *L, int s, int m)
{
    int temp = L->arr[s];
    // 
    for (int j = 2 * s; j <= m; j *= 2) {   // j为s的左孩子,j+1为s的右孩子
        if (j < m && L->arr[j] < L->arr[j+1])  // j不是最后一个结点,且左孩子小于右孩子
            ++j;
        if (temp >= L->arr[j])
            break;
        L->arr[s] = L->arr[j]; // 将该二叉树的最大值赋给根节点
        s = j;  // s指向原最大值的位置
    }
    L->arr[s] = temp;   // 插入
}

2.算法介绍

堆排序是对简单选择排血的一种改进,也属于选择类排序。做出的改进思想是:简单选择排序中,在含有n个元素的序列中找到最小的元素需要比较n-1次,但这一趟的其他比较结果并没有被保存下来,我们只选出了最小值,在下一次比较时,有许多比较其实已经被做过了,但由于前一趟没有保存下来,所以这些比较操作在这一趟的比较中又被重复执行了,导致排序的总比较次数较多。

堆是具有下列性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;每个结点的值都小于或等于其左右孩子的值,称为小顶堆。

堆排序算法是一种利用大顶堆或小顶堆结构进行排序的算法,其算法思想是:先将待排的序列从下到上、从右到左地构造成一个大顶堆,此时整个序列的最大值就是堆顶的根节点,把根节点移走,将堆数组的末尾元素(即序列的最小值)当做新的根结点,将其重新调整为一个大顶堆,注意此时被移走的原根结点不参与排序,然后反复执行移走跟结点再重新调整的操作,这样被移走的元素就构成了一个有序的序列。

堆排序的时间复杂度为O(nlogn),堆排序对原始序列的顺序并不敏感,所以它的最好、最坏和平均时间复杂度都是O(nlogn),又因为序列元素的交换是跳跃进行的,所以也是一种不稳定的排序。

补充:在一颗完全二叉树中,若当前结点的序号是s,则其左孩子的序号一定为2s,右孩子的序号一定为2s+1,孩子的孩子也是以2为倍数增加。

3.实例

#include <iostream>
using namespace std;

const int N = 100;

typedef struct
{
    int arr[N];
    int length;
} SqList;

void swap(SqList *L, int i, int j)
{
    int temp = L->arr[i];
    L->arr[i] = L->arr[j];
    L->arr[j] = temp;
}

void HeapSort(SqList *L);
void HeapAdjust(SqList *L, int s, int m);


void HeapSort(SqList *L)
{
    for (int i = L->length / 2; i > 0; i -- )   // 构造大顶堆
        HeapAdjust(L, i, L->length);

    for (int i = L->length; i > 0; i -- ) {
        swap(L, 1, i);
        HeapAdjust(L, 1, i - 1);
    }
}

void HeapAdjust(SqList *L, int s, int m) 
{
    int temp = L->arr[s];
    for (int j = 2 * s; j <= m; j *= 2) {
        if (j < m && L->arr[j] < L->arr[j+1])
            ++j;
        if (temp >= L->arr[j])
            break;
        L->arr[s] = L->arr[j];
        s = j;
    }
    L->arr[s] = temp;
}

int main()
{
    SqList L;
    L.arr[1] = 50;
    L.arr[2] = 10;
    L.arr[3] = 90;
    L.arr[4] = 30;
    L.arr[5] = 70;
    L.arr[6] = 40;
    L.arr[7] = 80;
    L.arr[8] = 60;
    L.arr[9] = 20;
    L.length = 9;

    HeapSort(&L);  // 堆排序测试
    for (int i = 1; i <= L.length; i ++ )
        cout << L.arr[i] << " ";

}

注:内容参考自《大话数据结构》

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值