acwing 838. 堆排序-java版本

题目所属分类

堆是一个完全二叉树 最后一层结点是从左到右排布的
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
把数组建成堆 O(n)的时间复杂度

在这里插入图片描述为啥从下到上遍历:如果从上往下遍历,当某个节点即是父节点又是子节点并且它的子节点仍然有子节点的时候,因为子节点还没有遍历到,所有子节点不符合小根堆的性质,当子节点调整的时候,必然会影响父节点的二次调整。

数据结构中的堆 y总的经典模板

// h[N]存储堆中的值, h[1]是堆顶,x的左儿子是2x, 右儿子是2x + 1
// ph[k]存储第k个插入的点在堆中的位置
// hp[k]存储堆中下标是k的点是第几个插入的
int h[N], ph[N], hp[N], size;

// 交换两个点,及其映射关系
void heap_swap(int a, int b)
{
    swap(ph[hp[a]],ph[hp[b]]);
    swap(hp[a], hp[b]);
    swap(h[a], h[b]);
}

void down(int u)
{
    int t = u;
    if (u * 2 <= size && h[u * 2] < h[t]) t = u * 2;
    if (u * 2 + 1 <= size && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
    if (u != t)
    {
        heap_swap(u, t);
        down(t);
    }
}

void up(int u)
{
    while (u / 2 && h[u] < h[u / 2])
    {
        heap_swap(u, u / 2);
        u >>= 1;
    }
}

// O(n)建堆
for (int i = n / 2; i; i -- ) down(i);

作者:yxc
链接:https://www.acwing.com/blog/content/404/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

原题链接

在这里插入图片描述

题解

 import java.util.Scanner;
public class Main{
    static int N = 100010;
    static int[] h = new int[N];
    static int size;
     public static void main(String[] args){
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        int m = scan.nextInt();
         for(int i = 1 ; i <= n ; i++ ) h[i] = scan.nextInt();
         size = n ;
         //从n/2的位置开始将数组中的值插入堆中
        //堆结构是一个完全二叉树,所有有分支的数等于没有分支的数,即最后一排的数量等于上面所有的数
        //最下面一排没有分支的数不用参与插入,所以从n/2开始进行插入
         for(int i = n / 2 ; i > 0 ;i--){//把数组转换成堆
             down(i);//就是让他进行向下排序处理 
         }
         while(m -- > 0){
            //输出前m小的m个元素
            System.out.print(h[1] + " "); //每一次输出头节点
            //因为是数组,删除第一个数复杂,删除最后一个元素比较简单
            //所以就将最后一个元素将第一个元素覆盖掉,然后删除掉最后一个元素
            h[1] = h[size--];
            //然后进行堆排序,对第一个元素进行处理
            down(1);
        }
     }
     //交换函数
    public static void swap(int x,int y){
        int temp = h[x];
        h[x] = h[y];
        h[y] = temp;
    }
    //堆函数核心函数,堆排序函数,传入的参数u是下标
    public static void down(int u){
        int t = u; // t用来分身变量
        //u的左分支下标小于size说明该数存在,然后如果这个数小于t,则让左下标赋值给t,即u的分身 
        if(u * 2 <= size && h[u * 2] < h[t]) t = u * 2; 
        //u的右分支下标小于size说明该数存在,然后如果这个数小于t,因为可能上面的if语句会重置t,则判断是不是小于新或旧t,
        //如果小于t的话,就让右下标赋值给t ,即u的分身。
        if(u * 2 + 1 <= size && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
        //如果u不等于t,说明左或者右下标有比较小的值赋值给t了,分身变了
        if(u != t){
            //就让u跟t这两个数交换一下位置,让小的数替代u的位置
            swap(u,t);
            //然后继续递归t下标小的位置
            down(t);
        }
    }

 
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

依嘫_吃代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值