[排序算法]C++实现堆排序

定义

堆的定义如下:n个元素的序列 {k1,k2,,kn} { k 1 , k 2 , … , k n } 当且仅当满足以下关系时,称之为堆。

{kik2ikik2i+1{kik2ikik2i+1(i=1,2,,n2) { k i ≤ k 2 i k i ≤ k 2 i + 1 或 { k i ≤ k 2 i k i ≤ k 2 i + 1 ( i = 1 , 2 , … , ⌊ n 2 ⌋ )

  若将和此序列对应的一维数组看成是一个完全二叉树,则堆的含义表明,完全二叉树中所有非终端结点的值无论不大于(或不小于)其左、右孩子结点的值。由此,若序列 {k1,k2,,kn} { k 1 , k 2 , … , k n } 是堆,则堆顶元素(或完全二叉树的根)必为序列中n个元素的最小值(或最大值)。
  若在输出堆顶的最小值之后,使得剩余n-1个元素的序列又重建成一个堆,则得到n个元素中的次小值。如此反复执行,便能得到一个有序序列,这个过程称之为堆排序


实现堆排序的步骤

  自堆顶至叶子的调整过程称为“筛选”。
  从一个无序序列建堆的过程就是一个反复“筛选”的过程,若将此序列看成是一个完全二叉树,则最后一个非终端结点是第 n2 ⌊ n 2 ⌋ 个元素,由此“筛选”只需从第 n2 ⌊ n 2 ⌋ 个元素开始。
  
  1. 由一个无序序列建成一个堆
  2. 输出堆顶元素,调整剩下元素成为一个新的堆


C++实现


/*
    iArr数组中,除iArr[iStart]以外均满足堆的定义
    该函数调整iArr[iStart]的关键字,使iArr成为一个大顶堆(对其中关键字而言)
*/

void HeapAdjust(int iArr[], int iStart, int iEnd)
{
    int rc = iArr[iStart]; // 先将需要调整的值保存
    for (int lChild = 2 * iStart + 1; lChild <= iEnd; lChild = lChild*2+1) //沿key较大的孩子结点向下筛选
    {
        // 比较左、右孩子的大小,lChild为key较大的记录的下标
        if (lChild < iEnd && iArr[lChild] < iArr[lChild + 1])
        {
            lChild++; 
        }

        if (rc > iArr[lChild])
        {
            break;                   // 满足大顶堆的条件,则跳出循环
        }
        iArr[iStart] = iArr[lChild]; // 将孩子结点中key较大的记录直接覆盖父结点
        iStart = lChild;            
    }
    iArr[iStart] = rc;               // 将需要调整的值放入最终的位置
}

void HeapSort(int iArr[], int iLength)
{
    // 把iArr建成大顶堆
    for (int i = iLength / 2 - 1; i >= 0; i--)
    {
        HeapAdjust(iArr, i, iLength-1);
    }

    int iTemp = 0;
    for (int i = iLength-1; i > 0; i--)
    {
        // 交换堆顶元素iArr[0]和已排好序的前一位元素iArr[i]
        iTemp = iArr[0];
        iArr[0] = iArr[i];
        iArr[i] = iTemp;
        // 将iArr[0,i-1]重新调整为大顶堆
        HeapAdjust(iArr, 0, i - 1);
    }
}

总结

堆排序方法对记录数较少的文件并不提倡,但对n较大的文件还是很有效的。因为其运行时间主要耗费在建初始堆和调整建新堆时进行的反复“筛选”上。对深度为k的堆,筛选算法中进行的关键字比较次数至多为2(k-1)次,则在含n个元素、深度为h的堆时,总共进行的关键字比较次数不超过4n。又n个结点的完全二叉树的深度为 log2n+1 ⌊ log 2 ⁡ n ⌋ + 1 ,则调整建新堆时调用HeapAdjust过程n-1次,总共进行的比较次数不超过

2(log2(n1)+log2(n2)++log22)<2n(log2n) 2 ( ⌊ log 2 ⁡ ( n − 1 ) ⌋ + ⌊ log 2 ⁡ ( n − 2 ) ⌋ + ⋯ + ⌊ log 2 ⁡ 2 ⌋ ) < 2 n ( ⌊ log 2 ⁡ n ⌋ )

时间复杂度
由此,堆排序最坏的情况下,其时间复杂度也为O(nlogn)。相对于快速排序来说,这是堆排序的最大优点。

空间复杂度
堆排序仅需一个记录大小供交换用的辅助存储空间。

最坏时间复杂度最优时间复杂度平均时间复杂度空间复杂度
O(nlogn) O ( n l o g n ) O(nlogn) O ( n l o g n ) O(nlogn) O ( n l o g n ) O(1) O ( 1 )

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值