前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >7.4.2 选择排序之堆排序

7.4.2 选择排序之堆排序

作者头像
week
发布2018-08-27 10:34:18
2920
发布2018-08-27 10:34:18
举报
文章被收录于专栏:用户画像

堆排序是一个树形选择排序方法,它的特点是:在排序过程中,将L[1...n]看成是一棵完全二叉树的顺序存储结构,

利用完全二叉树中双亲结点和孩子结点之间的内在关系,在当前无序区中选择关键字最大(或最小)的元素。

堆的定义如下:n个关键字序列L[1...n]称为堆,当且仅当该序列满足: ①L(i)<=L(2i)且L(i)<=L(2i+1)或②L(i)>L(2i)且L(i)>=L(2i+1)(1<=i<=[n/2])

满足第一种情况的堆称为小根堆(小顶堆),满足第二种情况的堆称为大根堆(大顶堆)。

如图所示是一个大根堆

87

45

78

32

17

65

53

09

算法思想:

堆排序的关键是构造初始堆,对于初始序列建堆,就是一个反复筛选的过程。

n个结点的完全二叉树,最后一个结点是第【n/2】个结点为根的孩子。

对第【n/2】个结点为根的子树筛选(对于大根堆:若根节点的关键字小于左右子女中关键字较大者,则交换),使该子树成为堆。

之后向前依次对各结点(【n/2】-1~1)为根的子树进行筛选,看该结点值是否大于其左右结点的值,若不是,将左右结点中较大值与之交换,交换后可能会破坏下一级的堆,于是继续采用上述方法构造下一级的堆,直到以该结点的子树构造成堆为止。

反复利用上述调整堆的方法建堆,直到根节点为止。

建立大根堆的算法:

代码语言:javascript
复制
<span style="font-size:18px;">void BuildMaxHeap(ElemType A[],int len){
    for(int i=len/2;i>0;i--){//从i=[n/2]~1,反复调整堆
        AdjustDown(A,i,len);
    }
}
void AdjustDown(ElemType A[],int k,int len){
//函数AdjustDown将元素i向下进行调整
    A[0]=A[k];//A[0]暂存

    for(i=2*k;i<=len;i*=2){//沿key较大的子节点向下筛选
        if(i<len&&A[i]<A[i+1]){//左孩子大于右孩子
            i++;//取key较大的子节点的下标
        }
        if(A[0]>=A[i]){//如果暂存大于其孩子结点,即满足大顶堆
            break;//筛选结束
        }else{ //如果暂存小于其孩子结点,即不满足大顶堆
            A[k]=A[i];//将A[i]调整到双亲结点
            k=i;//修改k值,以便继续向下筛选
        }
    }//for
    A[k]=A[0];//被筛选结点的值放入最终位置

}</span>

例如53,17,78,09,45,65,87,32

i=4 AdjustDown(A,4,8);

k=4 

A[0]=A[4]=09  

i=8 

8<8&&A[8]<A[9]==false 跳过

A[8]=32 A[0]<=A[8]==true

A[4]=A[0]=32; k=8

A[8]=A[0]=09

53,17,78,32,45,65,87,09

i=3 AdjustDown(A,3,8);

k=3

A[0]=A[3]=78

i=6

i<8&&A[6]<A[7]==true i=7 

A[0]>=A[7]==false 

A[3]=A[7]=87 k=7

A[7]=A[0]=78

53,17,87,32,45,65,78,09

i=2 AdjustDown(A,2,8);

k=2;

A[0]=A[2]=17

i=4

4<8&&A[4]<A[5]==true i=5

A[0]>A[5]==false A[2]=A[5]=45k=5

i=10 i>len 循环结束

A[5]=A[0]=17

53,45,87,32,17,65,78,09

i=1 AdjustDown(A,1,8);

k=1;

A[0]=A[1]=53

i=2

2<8&&A[2]<A[3]==true i=3

A[0]>A[3]==false A[1]=A[3]=87 k=i=3

i=6 6<8&&A[6]<A[7]==ture i=7

A[0]>=A[7]==false A[3]=A[7]=78 k=i=7

A[7]=A[0]=53

87,45,78,32,17,65,53,09

建堆的时间复杂度分析: 向下调整的时间与树高有关,为O(h).建堆过程中每向下调整时,大部分结点的高度都较小, 因此,可以证明在元素个数为N的序列上建堆,其时间复杂度为O(n),这说明可以在线性表时间内,将一个无序数组建成一个大顶堆。 堆排序算法分析: 应用这种数据结构进行排序的思路很简单,首先将存放在L[1...n]中的N个元素建成初始堆,由于堆本身的特点(以大根堆为例)堆顶元素就是最大值。 输出堆顶元素后,通常将堆底元素送入堆顶,此时根结点已经不满足大顶堆的性质,堆被破坏。 将堆顶向下调整使其继续保持大顶堆的性质,再输出堆顶元素,如此重复,直到堆中仅剩下一个元素为止。 堆排序算法: void HeapSort(Elemtype A[],int len){     BuildMaxHeap(A,len);//初始建堆     for(i=len;i>1;i--){//n-1趟的交换和建堆过程         Swap(A[i],A[1]);//输出堆顶元素(和堆底元素交换)         AdjustDown(A,1,i-1);//整理,把剩余的i-1个元素整理成堆     }//for } 同时,堆也支持删除和插入的操作。 由于堆顶元素或为最大值或为最小值, 删除堆顶元素时,先将堆的最后一个元素与堆顶元素交换,由于此时堆的性质被破坏,需对此时的根结点进行向下调整操作。 对堆进行插入操作时,先将新结点放在堆的末端,再对这个新结点执行向上调整操作。 向上调整堆的算法:

代码语言:javascript
复制
<span style="font-size:18px;">void AdjustUp(ElemType A[],int k){
    //参数K为向上调整的结点,也为堆的元素个数
    A[0]=A[k];//A[0]暂存
    int i=k/2;//若结点值大于双亲结点,则将双亲结点向下滑,并继续向上比较
    while(i>0&&A[i]<A[0]){
        A[k]=A[i];//双亲结点下滑
        k=i;
        i=k/2;//继续向上比较
    }//while
    A[k]=A[0];//复制到最终位置
}</span>

如:87,45,78,32,17,65,53,09,63 k=9 A[0]=A[9]=63 i=4 A[4]=32<63 A[9]=A[4]=32 k=4 i=2 A[2]=45<63 A[4]=A[2]=45 k=2 i=1 A[1]=87>63 跳出循环 A[2]=A[0]=63 空间效率:仅使用了常数个辅助单元,所以空间复杂度为O(1). 时间效率:建堆时间为O(n),之后n-1次向下调整,每次调整的时间复杂度为O(h),故在最好、最坏和平均情况下,堆排序的时间复杂度为O(nlog2 n)。 稳定性:在进行筛选时,有可能把后面相同关键字的元素调整到前面,所以堆排序是一种不稳定的排序方法。 如L={1,2,2} len=3 i=1 AdjustDown(A,1,3) k=1, A[0]=A[1]=1 i=2 A[2]=A[3] A[0]>=A[1] 筛选结束 A[1]=A[0]=1 如果严格按照堆排序建立大顶堆,笔者认为是稳定的。但手动建堆,可能将2交换至堆顶——>L={2,1,2}->L={1,2,2} 最终2,2 的次序已经发生变化。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2015年02月28日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档