数据结构之浅析堆排序

堆排序是一种基于比较的排序算法,通过构建大顶堆并不断调整来达到排序的目的。文章介绍了堆排序的定义、具体实现过程以及复杂度分析。在实现部分详细解释了如何构建大顶堆和调整堆的过程,复杂度分析表明堆排序的时间复杂度为O(nlogn)。
摘要由CSDN通过智能技术生成

堆排序

  • 定义
  • 实现
  • 复杂度分析

1、定义

将待排序的序列构造成一个大顶堆(整个序列的最大值就是堆顶的根节点),然后将根节点与堆中末尾元素交换,再将剩余的n-1个序列重新构造成一个堆,从而又得到n个元素的次大值,如此反复就能得到一个有序序列。

1、如下图所示,是一个大顶堆,90是最大值,将根节点(90)和末尾元素(20)互换。

这里写图片描述

2、此时90成为了整个堆中的最后一个元素,然后将剩下的n-1个元素重新构造大顶堆,再重复步骤1,如下图所示:

这里写图片描述

3、如此循环下去,最终就会得到有序的序列。

2、实现

1、如何由一个无序的序列构造成一个大顶堆
2、如果在输出堆顶元素后,如何调整剩余元素成为一个新堆

对顺序表L进行堆排序的程序:

void HeapSort(SqList *L)
{
    int i;

    //构建一个大顶堆
    for(i = L->length/2 ; i>0 ; i--)
        HeapAdjust(L,i,L->length);

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

}

整个排序过程主要由两个过程完成(两个循环),第一个循环是将现有的待排序的序列构建成为一个大顶堆;第二个循环是将根节点和末尾元素交换,然后将剩余的元素重新构造大顶堆。

下面将详细分析函数HeapAdjust()的实现

void HeapAdjust(SqList*L, int s, int m)
{
    int temp, j;
    temp = L->r[s];
    for (j = 2 * s; j <= m; j *= 2) //沿关键点较大的子节点筛选
    {
        if (j < m && L->[j] < L->[j + 1]) //得到哪一个父节点的哪一个子节点较大
            ++j;  //取出较大的子节点编号
        if (temp >= L->r[j])  //判断是否比其父节点大,如果最大的子节点都比父节点小,则退出
            break;
        L->r[s] = L->r[j];  //下面三行都是为了实现父节点和最大子节点的交换(比父节点大时)
        s = j;
    }
    L->r[s] = temp;
}

1、如下图所示,节点4(父节点)比节点8(左子节点)小,需要交换

这里写图片描述

2、交换的结果如下图所示:

这里写图片描述

3、由于节点3(父节点)比其左右子节点都大,所以不需要交换。节点2(父节点)比其子节点4,5都小,找出最大的是右子节点,所以和右子节点交换。

这里写图片描述

4、如此循环,最终得到的大顶堆如下图所示:

这里写图片描述

然后进行堆排序的第二个循环(根节点与末尾节点交换,对n-1再使用上述的调整),就可以得到最终的有序结果。

3、复杂度分析

堆排序的运行时间主要是消耗在初始构建堆和重建堆时的反复筛选上。空间复杂度上,使用一个暂存单元,所以为1.

1、构建堆的时间:
在构建堆的过程中,是完全二叉树从最下层最右边的非终端节点开始构造的(n/2)个,将其与左右子节点的比较是必要的,并且最多进行2次比较和互换的操作,因此其时间复杂度为O(n)。

1、重建堆的时间:
第i次取堆顶记录重建堆需要用O(logi)的时间,并且需要取n-1次堆顶,因此,重建堆的时间复杂度为O(nlogn)。

所以整体的时间复杂度为O(nlogn)。由于堆排序对于原始数据的排序状态不敏感,所以无论是最好、最坏还是平均都是这个时间复杂度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值