• [数据结构]堆排序


    算法思想

      堆排序是一种树形选择排序方法,它的特点是:在排序过程中,将L[1…n]看成是一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系,在当前无序区中选择关键字最大(或者最小)的元素。
      堆的定义如下:n个关键字序列L[1…n]称为堆,当且仅当该序列满足:
      ①L(i)≤L(2i)且L(i)≤L(2i)或 ②L(i)≥L(2i)且L(i)≥L(2i+1)
      满足第一种情况的堆称为小根堆,满足第二种情况的称为大根堆。显然,在大根堆中,最大元素存放在根结点中,且对其中任意一个非根结点,它的值小于或等于其双亲结点值。小根堆的定义刚好相反,根结点是最小元素。
      这里写图片描述
      堆排序的关键是构造初始堆,对初始序列建堆,就是一个反复筛选的过程。n个结点的完全二叉树,最后一个结点是第[n/2]个结点的孩子。对第[n/2]哥结点为根的子树筛选(对于大根堆:若根结点的关键字小玉左右子女中关键字较大者,则交换),使该子树成为堆。之后向前依次对各结点([n/2]-1~1)为根的子树进行筛选,看该结点值是否大于其左右子结点的值,若不是,将左右子结点中较大值与其交换,交换后可能会破坏下一级的堆,于是继续采用上述方法构造下一级的堆,直到以该结点为根的子树构成堆位置。反复利用上述调整堆的方法建堆,直到根结点。
      这里写图片描述

    算法代码

    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将元素k向下进行调整
        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]) reak;//筛选结束
                else{
                    A[k]=A[i];//将A[i]调整到双亲结点上
                    k=i;//修改k的值,以便继续向下筛选
                }
        }
        A[k]=A[0];//被筛选结点的值放入最终位置
    }

      向下调整的时间和树高有关,为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个元素整理成堆
        }
    }

      同时,堆也支持删除和插入操作。由于堆顶元素或为最大值或最小值,删除堆顶元素时,先将堆的最后一个元素与堆顶元素交换。由于此时堆的性质被破坏,将堆顶元素向下调整操作。对堆进行插入操作时,先将新结点放在堆的末端,再对这个新结点执行向上调整操作。
      这里写图片描述
      下面是向上调整堆的算法:
      

    void AdjustUp(Elemtype A[],int k){
    //参数k为向上调整的结点,也为堆的元素个数
        A[0]=A[k];
        int i=k/2;//若结点值大于双亲结点,则将双亲结点向下调,并继续向上比较
        while(i>0&&A[i]<A[0]){
            A[k]=A[i];//双亲结点下调
            k=i;
            i=k/2;//继续向上比较
        }
        A[k]=A[0];//复制到最终位置
    }

    算法复杂度

      空间复杂度:仅使用了常数个辅助单元,所以空间复杂度为O(1)。
      时间复杂度:建堆时间为O(n),之后有n-1次向下调整的操作,每次调整的时间复杂度为O(log₂n)。所以时间复杂度为O(nlog₂n)。
      稳定性:在进行筛选时,有可能把后面相同关键字的元素调整到前面,所以堆排序算法是一种不稳定的排序方法。例如,表L={1,2,2},构造初始堆时,可能将2交换到堆顶,此时L={2,1,2},最终排序序列为L={1,2,2},显然,2和2的相对次序已经发生了变化。

    https://github.com/li-zheng-hao
  • 相关阅读:
    P1428 小鱼比可爱
    P5727 【深基5.例3】冰雹猜想
    P1427 小鱼的数字游戏
    P1047 [NOIP2005 普及组] 校门外的树
    P5729 工艺品制作
    P5728 【深基5.例5】旗鼓相当的对手
    CodeSmith使用和语法简介
    系统缓存
    FLV视频转换的利器 ffmpeg.exe
    Vs.Net方向将Excel数据导入到数据库
  • 原文地址:https://www.cnblogs.com/lizhenghao126/p/11053727.html
Copyright © 2020-2023  润新知