堆排序(C++)

堆排序是简单选择排序的升级,两者思想相同,都是将一组无序的序列看作已排序部分和未排序部分,然后从未排序部分选择一个元素加入已排序部分,直到所有元素都完成排序。

思路:

构造:

首先将序列中待排序的部分构造成一个堆。

堆即是一棵完全二叉树,此处用数组存储二叉树,根据二叉树的性质可知,结点 i 的双亲结点为 i / 2 、左子树为 2i、右子树为 2i+1(假设 i 为数组下标)。

堆分为大根堆和小根堆,大根堆即每个父结点的值大于其左右两个子树的值,小根堆即每个父结点的值小于其左右两个子树的值。这里采用大根堆。

将二叉树从下往上,对每个父节点调用堆调整函数即可(后面会解释该函数),将序列的元素个数(即树的结点数)除以2,便可以获得最下层的父结点的下标,设置一个for循环,i 初始指向最下层的父结点,然后不断回退,每次都调用一次堆调整函数,直到 i 回退到根结点为止(数组下标为1)。

交换:

若是大根堆,堆顶结点即为最大值,将其与序列待排序部分的尾元素交换,并该元素加入已排序部分(即缩小待排序部分的范围)。该操作破坏了待排序部分的次序,于是将待排序部分重新调整成堆(调用堆调整函数)。
重复上述的交换操作,直到堆中只剩一个元素。

由于二叉树的性质,为了方便,该算法的数组第0位不存储有效数据。

堆排序代码:
//堆排序函数
void HeapSort(vector<int> &v)
{
    int n=v.size();
    //创建堆
    for(int i=(n-1)/2;i>=1;--i) //第0位无效,所以只有n-1个元素
    {
        HeapAdjust(v,i,n-1);
    }
    //处理序列的待排序部分
    for(int i=1;i<n;++i)
    {
        swap(v[1],v[n-i]);//待排序部分的首元素(不包括0,从1开始)与尾元素交换。
        HeapAdjust(v,1,n-1-i);//将待排序部分的剩余元素重新调整为堆,注意要不断缩小待排序部分的范围
    }
}
堆调整:

假设此时要将数组从beg到end重新调整为大根堆,
设置两个伪指针,指针p指向当前要处理的父结点,指针c指向其子树中的较大者(初始指向左子树),判断其左右子树结点值大小并更新指针c的指向;然后再对比父结点和子树结点,若子树较大才执行交换操作。
更新指针p指向其子树。
重复步骤,直到再无子树。

//堆调整函数
void HeapAdjust(vector<int> &t,int beg,int end)
{
    int p=beg,c=2*p;
    while(c<=end)//如果左子树存在
    {
        if(c!=end && t[c]<t[c+1])//如果c不等于end(即右子树存在)且左子树小于右子树
        {
            ++c;//c存放较大子树的下标,初始指向左子树,此时右子树较大,所以便指向右子树
        }
        if(t[p]<t[c])//如果根结点小于左子树
        {
            swap(t[p],t[c]);
        }
        p=c;
        c=p*2;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值