考研算法30天:堆排序 【堆排序】

原先自己写过这道题的题解,但是当时水平有限所以这次重写一次。

(1条消息) 堆的创建(题目:堆排序)_空が笑っています的博客-CSDN博客

算法介绍

我在上陈越姥姥的课程之后我学会了如何用数组表示一个堆(堆其实就是根节点大于或者小于它的子节点的完全二叉树)然后陈越姥姥创建堆的思路是每次将新节点放到数组的最后然后通过和自己的父节点比较然后找到自己的位置然后插入。

void insert(int X){
    int i;
    for(i = ++size ; X<H[i/2] ; i = i/2){
        H[i] = H[i/2];
    }
    H[i] = X;
}

我觉得陈越姥姥的这个思路更加简单而且更容易理解的,如果一道题只需要插入的话,我觉得这个方式是最好的。 

但是看这道题它是需要你顺序排序的,那么我觉得最简单的思路有两种

第一种建立最小堆,然后每次将最小的(堆顶元素)弹出并输出。

第二种是建立最大堆,然后每次将最大的(堆顶元素)放入栈,然后根据栈先进后出的思路将元素弹出。(y总虽然没使用栈,但是我觉得思路差不多。)

我们很快发现不管是哪种方式,我们都需要删除的操作,所以姥姥的方式在这道题上就不太适用了。我们应该将这种调整元素位置的代码抽象出来并且可以用于删除。下面这种方式也是是从下面向上比较(每次父节点和自己的左右节点比较然后自己和自己的左右节点就在自己为根节点的二叉树中形成了堆,从n/2开始是因为下标比N/2大的节点是没有自己的子节点的了)


void down(int u){
    int t=u;
    if(mysize >= u * 2 && h[t] > h[u * 2]) t = u * 2;
    if(mysize >= (u * 2 + 1) && h[t] > h[u * 2 + 1]) t = u * 2 + 1;
    if(t != u){
        swap(h[u],h[t]);
        down(t);
    }
}

算法题目

#include <iostream>

using namespace std;

const int N = 1000010;
int q[N],sz;

void down(int x){
    //n/2就是有子节点最大的下标。
    //注意:这道题我们的n和size并不等价,size因为删除的原因
    //size会一直变。
    if(x>(sz/2))return;
    int t = x;
    //将根节点和其左右节点三者比较将最大的节点放到根节点上
    if(x*2<=sz  && q[x*2] > q[t]) t = x*2;
    if((x*2+1)<=sz && q[x*2+1] > q[t]) t = x*2 +1;
    if(t!=x){
        swap(q[t],q[x]);
        //如果下面子节点还有节点的话,就继续遍历
        //没有就返回,这道题上是不会有的,
        //因为我们一开始就是从n/2开始向下down的
        //而n/2就是有子节点最大的下标。
        down(t);
    }
}

void heap_sort(int n){
    sz = n;
    for(int i=n/2;i;i--) down(i);
    for(int i=n-1;i>=0;i--){
        //将根节点(最大的节点)和最后的节点交换位置
        swap(q[1],q[sz]);
        //然后删除最大的节点。
        sz--;
        //因为刚才将最后的节点放到了根节点的位置
        //所以我们需要从1开始向下再down一次
        down(1);
    }
    return;
}
int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)scanf("%d",&q[i]);
    heap_sort(n);
    for(int i=1;i<=n;i++)printf("%d ",q[i]);
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值