堆排序-java

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

文章目录

前言

一、堆排序

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 

总结

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/21130.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小公司的软件开发IT工具箱

目录 工具链困境 难题的解决 达到的效果 资源要求低 工具箱一览 1、代码管理工具 2、自动化发版&#xff08;测试&#xff09;工具 3、依赖库&#xff08;制品包&#xff09;管理 4、镜像管理 5、授权管理&#xff08;可选&#xff09; 待讨论&#xff1a;为什么不是…

LeetCode17电话号码的字母组合

题目描述 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 解析 广度优先遍历或者深度优先遍历两种方式&#xff0c;广度优先…

渗透测试靶机----FirstLeads_1.3

渗透测试靶机----FirstLeads_1.3 启动靶机&#xff0c;扫描ip&#xff0c;平平无奇 可以看出&#xff0c;这里是存在139这个主机的&#xff0c;这就是扫描出来的靶机ip继续探测端口及其他信息 发现这里只开启了80 端口 尝试一些基本目录&#xff0c;发现存在robot.txt文件目录…

集成算法:Bagging模型、AdaBoost模型和Stacking模型

概述 目的&#xff1a;让机器学习效果更好&#xff0c;单个不行&#xff0c;集成多个 集成算法 Bagging&#xff1a;训练多个分类器取平均 f ( x ) 1 / M ∑ m 1 M f m ( x ) f(x)1/M\sum^M_{m1}{f_m(x)} f(x)1/M∑m1M​fm​(x) Boosting&#xff1a;从弱学习器开始加强&am…

插入排序以及希尔排序; 先学会插入,希尔会更简单喔

1.前言 首先肯定是要学会插入排序再学习希尔排序会更简单&#xff0c;因为代码部分有很多相似之处&#xff1b;如果你觉得你很强&#xff0c;可以直接看希尔排序的讲解。哈哈哈&#xff01;&#xff0c;每天进步一点点&#xff0c;和昨天的自己比 2.插入排序 让我们先来看看…

鸿蒙Ability Kit(程序框架服务)【UIAbility组件与UI的数据同步】

UIAbility组件与UI的数据同步 基于当前的应用模型&#xff0c;可以通过以下几种方式来实现UIAbility组件与UI之间的数据同步。 [使用EventHub进行数据通信]&#xff1a;在基类Context中提供了EventHub对象&#xff0c;可以通过发布订阅方式来实现事件的传递。在事件传递前&am…

Rustdesk 自建服务器教程

一、环境 阿里云轻量服务器、debian11 系统 二、服务端搭建 2.1、开放防火墙指定端口 TCP(21115, 21116, 21117, 21118, 21119)UDP(21116) 2.2、安装 rustdesk 服务器文件 在 github 下载页https://github.com/rustdesk/rustdesk-server/releases/&#xff0c;下载 rustde…

【自撰写,国际象棋入门】第1课、棋盘和棋子

第1课 棋盘和棋子 一、国际象棋的棋盘 国际象棋的棋盘为一8乘8的黑、白格相间的棋盘&#xff0c;8条竖线的编号分别为A-H&#xff0c;8条横线的编号分别为1-8&#xff0c;在记谱时用竖线编号横线编号的方式表示棋盘上的格子&#xff0c;例如a1格、h8格等.棋盘上有几条重要的大…

c++程序员为什么要做自己的底层库

五一期间&#xff0c;在家里翻到之前上学时候用的电脑和工作日志&#xff0c;粗略浏览一番&#xff0c;感慨10年岁月蹉跎&#xff0c;仍然没有找到自己技术方向的“道”。遂有感而发&#xff0c;写下此文。 算起来&#xff0c;接触软件开发也有10年时间了&#xff0c;最开始是…

Java——异常

1.什么是异常 将程序执行过程中发生的不正常行为称为异常。 常见的异常有&#xff1a;算数异常&#xff0c;空指针异常&#xff0c;数组越界异常 每一种异常都有对应的类对齐描述 为了对每一种异常进行管理&#xff0c;Java内部实现了一个对异常的体系结构 1. Throwable&#x…

CS2游戏30万挂箱账号被封,饰品市场要变天

Steam游戏平台上CS2的玩家在线人数常年位于第一位&#xff0c;即便偶尔会被爆款游戏挤下来&#xff0c;但一切都是暂时的。饰品交易作为CS2的重要组成部分&#xff0c;早已成为了维系游戏热度的不二法门。可相对应的&#xff0c;各种挂箱子的工作室及个人也孕育而生。 但近来V社…

【Docker学习】docker pull详细说明

docker pull是我们经常用到的一个命令。我们使用一些官方镜像&#xff0c;如MySql、Nginx等都需要用docker pull下载。不过不用的话&#xff0c;也可以。比如使用docker run&#xff0c;要是找不到镜像&#xff0c;会自动下载。 命令&#xff1a; docker image pull 描述&am…

Uniapp写一个简单的商品瀑布流界面+商品详情

最终效果&#xff1a; 整体内容比较简单&#xff0c;参考了一篇瀑布流文章和一篇商品详情文章随便修改整了下&#xff0c;主要是给想做这方便面的新人一个简单逻辑的展示&#xff08;其实我也是第一次写这个emmm&#xff09; 一.组件下载&#xff1a; uni-icon uni-goods-nav…

什么是ACP?

前言 ACP指的是应用程序控制平面&#xff0c;是微服务架构中的一个关键组成部分。它负责管理微服务架构中的各个微服务&#xff0c;包括服务发现和注册、负载均衡、服务路由、熔断和降级、配置管理等方面的功能。 A&#xff1a;可用性 所有请求都有响应。C&#xff1a;强一致…

[DDR5 Jedec 3-4] 模式寄存器 Mode Register MRR/MRW

依公知及经验整理,原创保护,禁止转载。 专栏 《深入理解DDR》 1. 概念 模式寄存器用于定义各种操作模式。在初始化过程中,可以通过重新执行MRS命令来更改模式寄存器的内容。即使用户只想修改模式寄存器变量的一个子集,在发出MRS命令时也必须编程所有变量。 只有当所有ban…

现实残酷!存款百万只是少数人的游戏,普通家庭能存多少?

近期&#xff0c;网络上掀起了一股关于普通家庭终身存款上限的热烈讨论。一位网友通过简单的算术方式提出了一个假设&#xff1a;如果一对夫妻每年收入15万&#xff0c;并成功将6万存入银行&#xff0c;那么从25岁步入社会至60岁退休&#xff0c;他们理论上能积累到210万的存款…

Go语言之GORM框架(四)——预加载,关联标签与多态关联,自定义数据类型与事务(完结篇)

前言 本来是想着写多表关系的&#xff0c;不过写了一半发现重复的部分太多了&#xff0c;想了想与其做一些重复性工作&#xff0c;不如把一些当时觉得抽象的东西记录一下&#xff0c;就当用一篇杂记完成专栏的最后一篇文章吧。 预加载 简单示例 预加载主要用于在多表关系中…

谷歌浏览器的平替,内置开挂神器,我已爱不释手!

油猴浏览器正式版是一款基于谷歌Chromium源码开发的浏览器&#xff0c;它集成了集成了强大的油猴扩展&#xff08;Tampermonkey&#xff09;&#xff0c;使得用户可以轻松安装各种脚本&#xff0c;从而增强网页浏览体验。提供了一个更加个性化和高效的浏览体验。 油猴扩展&…

git使用流程

1.下载git 搜索下载 2.注册github账号&#xff08;打开爬墙工具&#xff09; 创建一个仓库 3.配置邮箱和密码 4.所以找一个文件夹 鼠标右键 选择 open Git Bash here&#xff08;当前文件夹下打开命令行&#xff09; 输入命令 配置用户名和邮箱 5.将建的仓库克隆下来 …

【JS实战案例汇总——不定时更新版】

一&#xff1a;转换时间案例 1 需求&#xff1a; 用户输入秒数&#xff0c;系统会自动将秒数转变为小时、分钟、秒&#xff0c;并且不满10的要在前面补零 2 算法&#xff1a; 小时:hour parseInt(总秒数/60/60%24) 分钟:minute parseInt(总秒数/60%60) 秒数:second pa…