首页 > 编程语言 >堆排序-java

堆排序-java

时间:2024-06-02 20:29:41浏览次数:16  
标签:结点 java int 堆排序 heap 小根堆 下标 size

这次主要讲了堆排序和堆的基本构造,下一期会详细讲述堆的各种基本操作。

文章目录

前言

一、堆排序

1.题目描述

2.堆

二、算法思路

1.堆的存储

2. 结点下移down

3.结点上移up

4.堆的基本操作

5.堆的初始化

三、代码如下

1.代码如下:

2.读入数据:

3.代码运行结果

总结


前言

这次主要讲了堆排序和堆的基本构造,下一期会详细讲述堆的各种基本操作。


提示:以下是本篇文章正文内容,下面案例可供参考

一、堆排序

1.题目描述

输入一个长度为 n 的整数数列,从小到大输出前 m小的数。

输入格式

第一行包含整数 n 和 m。

第二行包含 n 个整数,表示整数数列。

输出格式

共一行,包含 m 个整数,表示整数数列中前 m 小的数。

数据范围

1≤m≤n≤100000,
1≤数列中元素≤1000000000

2.堆

图2.1完全二叉树示意图 

完全二叉树:一棵深度为k的有n个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,每一层最多有2^{n-1}个结点,除了最后一层,其余层的结点数都是满的,且最后一层从左往右依次分布。

堆是一种基于完全二叉树的数据结构。可以分为大根堆,小根堆。

大根堆:每个结点的值都大于或者等于其左右孩子的值。

小根堆:每个结点的值都小于或者等于其左右孩子的值。 

二、算法思路

1.堆的存储

 图1.1堆的存储示例图

我们用一个一维数组来存储堆的值,下标来表示是哪个结点,下标为1就存储根节点的值,下标为2存储它的左孩子的值,下标为3存储它的右孩子的值,我们就可以类比推理出任一结点的左孩子和右孩子的下标。例如下标为x的结点,它的左孩子在数组中存储的下标就是2x,它的右孩子在数组中存储的下标是2x+1。(注:下标从1开始

2. 结点下移down

 图2.1示例一

 我们以一个小根堆为例,父结点的值要小于它的左右孩子结点。当我们将根节点修改为6后,此时小根堆的性质就被破坏了,那么我们就要对这个小根堆进行调整。

图2.2示例二 

此时,我们需要与根节点的左右孩子进行比较,得出6的左孩子3是3个点中最小的,进行交换。此时小根堆的性质还没维护好,此时我们还需要将6跟它的左右孩子进行比较,得出它的左孩子3是最小的,再将6和它的左孩子进行交换,此时检查后发现,符合小根堆的性质。即我们将某一个值变大,那么在小根堆中它就要下移。

    //传入结点下标
    public static void down(int x){
        int temp = x;
        //两个if语句来找出3个结点中最小的结点的下标
        if(2 * x <= size && heap[2* x] < heap[temp]){
            temp = 2 * x;
        }
        if (2 * x + 1 <= size && heap[2*x + 1] < heap[temp]){
            temp = 2 * x + 1;
        }
        //说明此时结点不是最小值,进行交换,再递归处理看是否还需要交换
        if(temp != x){
            int t = heap[temp];
            heap[temp] = heap[x];
            heap[x] = t;
            down(temp);
        }
    }

3.结点上移up

 

图3.1结点上移示例 

当我们把最右下角的结点值修改为2,此时小根堆的性质被破坏。我们把2和它的父结点进行比较得出2就是3个结点的最小值,2跟它的父结点进行交换;然后。此时的2再跟它的父结点进行比较得出2是3个结点中的最小值,将2跟它的父结点进行交换。此时检查后,发现符合小根堆的性质。可以看出如果值变大的话,我们需要进行结点上移操作,即结点上移来维护小根堆的性质。

up操作我们只需要跟父亲结点比就可以了。

    //传入结点下标
    public static void up(int x){
        int t = x;
        int temp =  x / 2;
        if(temp >= 1 && heap[temp] > heap[t]){
            t = temp;
        }
        if(t != x){
            int arr = heap[t];
            heap[t] = heap[x];
            heap[x] = arr;
            up(t);
        }
    }

 

4.堆的基本操作

我们引入一个一维整型数组heap来存储堆,一个整型变量size来表示堆中最后一个元素的下标或者堆中的元素个数。(数组下标0不用,我们从下标为1开始存储)

向堆中插入一个数:我们则直接在数组最后一个元素后面加入一个值,最后一个元素的下标为size即head[++size] = x;此时我们要预防堆的性质是否被破坏,那么我们直接执行结点上移操作即可。up(size);

求堆中的最小值:小根堆中的根节点就是堆中的最小值即head[1]。

删除最小值:即我们将根节点删除了,在一维数组中第一个元素我们很难删除,但是最后一个元素很容易删除,我们只需要用最后一个元素将根节点覆盖,然后将堆的大小减1即head[1] = head[size];dowx(1)来让结点下移来维护堆的性质。

删除任意一个元素:删除下标为k的结点值,我们还是用堆中的最后一个元素将这个元素值进行覆盖,然后将堆的大小减1即head[k] = head[size];size--;如果结点值变大的进行结点下移,结点值变小进行结点下移,那么我们直接down(k);up(k);因为它最后只会执行这两个操作中的一个,这样小根堆的性质也会被维护。

修改任意一个元素:修改下标为k的元素为x,那么我们需要head[k] = x;如果结点值变大的进行结点下移,结点值变小进行结点下移,那么我们直接down(k);up(k);因为它最后只会执行这两个操作中的一个,这样小根堆的性质也会被维护。

5.堆的初始化

图5.1示例图 

因为我们需要初始化建造一个小根堆,那么我们只需要从倒数第2层开始每一个结点进行结点下移操作就可以了。最后一层是叶子节点不需要进行结点下移操作。

        for(int i = 1; i <= n; i++){
            heap[i] = sc.nextInt();
        }
        size = n;
        //初始化建堆
        for(int i = n / 2;i > 0;i--){
            down(i);
        }

三、代码如下

1.代码如下:


import java.io.*;
import java.util.*;
public class 堆排序 {
    static PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static int N = 100010;
    //存储堆
    static int[] heap = new int[N];
    //堆中最后一个结点的下标,也是堆中元素的个数
    static int size = 0;
    public static void main(String[] args) throws Exception{
        Scanner sc = new Scanner(br);
        int n = sc.nextInt();
        int m = sc.nextInt();
        for(int i = 1; i <= n; i++){
            heap[i] = sc.nextInt();
        }
        size = n;
        //初始化建堆
        for(int i = n / 2;i > 0;i--){
            down(i);
        }
        while (m-- > 0){
            //打印最小值
            pw.print(heap[1] +" ");
            //删除堆中的根节点,然后维护小根堆性质
            heap[1] = heap[size];
            size--;
            down(1);
        }
        pw.flush();
    }
    //传入结点下标
    public static void down(int x){
        int temp = x;
        //两个if语句来找出3个结点中最小的结点的下标
        if(2 * x <= size && heap[2* x] < heap[temp]){
            temp = 2 * x;
        }
        if (2 * x + 1 <= size && heap[2*x + 1] < heap[temp]){
            temp = 2 * x + 1;
        }
        //说明此时结点不是最小值,进行交换,再递归处理看是否还需要交换
        if(temp != x){
            int t = heap[temp];
            heap[temp] = heap[x];
            heap[x] = t;
            down(temp);
        }
    }
    //传入结点下标
    public static void up(int x){
        int t = x;
        int temp =  x / 2;
        if(temp >= 1 && heap[temp] > heap[t]){
            t = temp;
        }
        if(t != x){
            int arr = heap[t];
            heap[t] = heap[x];
            heap[x] = arr;
            up(t);
        }
    }
}

2.读入数据:

5 3
4 5 1 3 2

3.代码运行结果

1 2 3 

总结

上述通过一些堆的基本操作来完成堆排序,后续会专门再写一次博客来详细介绍模拟堆的各种操作。

标签:结点,java,int,堆排序,heap,小根堆,下标,size
From: https://blog.csdn.net/m0_63267251/article/details/139388919

相关文章

  • java选择题
    1.以下哪项不是java基础类型()A.intB.booleanC.StringD.float正确答案:CJava的基础数据类型包括:byte、short、int、long、float、double、char和boolean。String不是一个基础数据类型,而是一个对象类型,它在Java中表示字符串。单选题2.假定AB为一个类,则执行“ABa......
  • Java题目集4~6的总结
    前言面向对象编程课程的“答题判题程序-4”作业是一个综合性的练习,旨在加深学生对面向对象编程思想的理解,并实际应用于解决复杂问题。本作业要求学生设计并实现一个答题程序,模拟小型测试的全过程,包括题目信息、试卷信息、答题信息、学生信息的输入,以及答题结果的判断和输出。家......
  • JVM(Java虚拟机)、JMM(Java内存模型)笔记
    面试常见:请你谈谈你对JVM的理解?java8虚拟机和之前的变化更新?什么是OOM,什么是栈溢出StackOverFlowError?怎么分析?JVM的常用调优参数有哪些?内存快照如何抓取?怎么分析Dump文件?谈谈JVM中,类加载器你的认识?请你谈谈你对JVM的理解?JVM(Java虚拟机)是Java程序的运行环境,它允......
  • JAVA IO流(File类,字节流,字符流)
    File类分隔符:a.路径名称分隔符:windows:linux:/b.路径分隔符:一个路径和其他路径之间的分隔符;1.概述:文件和目录(文件夹)路径名的抽象表示2.File的静态成员staticStringpathSeparator:与系统有关的路径分隔符,为了方便,它被表示为一个字符串。staticStrings......
  • Java面试题:解释一下Java中的synchronized关键字,它是如何保证线程安全的?
    在Java中,synchronized关键字是一种同步锁机制,用于确保多个线程在访问共享资源时能够保持线程安全。线程安全是指在多线程环境下,当多个线程尝试同时访问共享资源时,任何时刻最多只有一个线程能够执行特定的代码段。synchronized关键字可以用于以下几个方面:方法同步:当synch......
  • 南昌航空大学大一下学期java题目集4-6总结性Blog-苏礼顺23201608
    一、前言——总结三次题目集的知识点、题量、难度等情况 关于知识点  这次的三次题目集更加进一步体现了面向对象程序设计的思想方法。主要是之前的三次题目集就只是利用了面向对象三大基础特性中的封装特性,而这三次的题目集增加了继承与多态,这正是面向对象设计的精髓所......
  • JAVA使用ForkJoinPool实现子任务拆分进行数值累加代码示例
      SumTask.javaimportjava.util.concurrent.RecursiveTask;/***定义任务和拆分逻辑*RecursiveTask<Long>这个是有返回值的*如果不需要返回值可以用RecursiveAction*/publicclassSumTaskextendsRecursiveTask<Long>{/***累加的开始值......
  • JAVA SMTP例子
    一、SimpleMailSender.javapackageorg.fh.util.mail;importjava.util.Date;importjava.util.Properties;importjavax.mail.Address;importjavax.mail.BodyPart;importjavax.mail.Message;importjavax.mail.Multipart;importjavax.mail.Session;importjavax......
  • 《java数据结构》--哈希表
    ......
  • 【JavaEE 进阶(二)】Spring MVC(下)
    ❣博主主页:33的博客❣▶️文章专栏分类:JavaEE◀️......