模拟堆-java

模拟堆也是对堆的一次深入理解和一些其它操作,可以了解一下。

文章目录

前言

一、模拟堆

二、算法思路

1.结点上移

2.结点下移

3.插入一个数

 4.输出当前集合的最小值

5.删除当前集合的最小值(数据保证此时的最小值唯一)

6.删除第k个插入的数

7.修改第k个插入的数,修改为x

三、代码如下

1.代码如下:

2.读入数据:

3.代码运行结果

总结


前言

模拟堆也是对堆的一次深入理解和一些其它操作,可以了解一下。


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

一、模拟堆

维护一个集合,初始时集合为空,支持如下几种操作:

  1. I x,插入一个数 x;
  2. PM,输出当前集合中的最小值;
  3. DM,删除当前集合中的最小值(数据保证此时的最小值唯一);
  4. D k,删除第 k 个插入的数;
  5. C k x,修改第 k 个插入的数,将其变为 x;

现在要进行 N 次操作,对于所有第 22 个操作,输出当前集合的最小值。

输入格式

第一行包含整数 N。

接下来 N 行,每行包含一个操作指令,操作指令为 I xPMDMD k 或 C k x 中的一种。

输出格式

对于每个输出指令 PM,输出一个结果,表示当前集合中的最小值。

每个结果占一行。

数据范围

1≤N≤100000
−1000000000≤x≤1000000000
数据保证合法。

二、算法思路

我们还是通过一维整型数组heap来存储堆的值,数组下标来表示是哪一个结点,size表示堆中结点的个数或者堆中最后一个元素的下标。这道题中我们还是以小根堆为例子。堆在数组中存储,任一结点的下标为x,那么它的左孩子在数组中存储的下标为2x,右孩子在数组中存储的下标为2x + 1。(注:数组存储结点下标从1开始)

1.结点上移

当我们插入一个新结点这个结点值比它的父结点值小或者修改某一个结点结点值比它的父节点小这两种情况我们才会让结点上移来维护小根堆的性质。

    //结点上移,只需要跟父结点比较即可public static void up(int x){while ( x / 2 >= 1 && heap[x / 2] > heap[x]){int temp = heap[x / 2];heap[x / 2] = heap[x];heap[x] = temp;x /= 2;}}

我们需要判断当前下标有没有父结点即 x / 2是否满足大于等于1,并且当父结点的值比子结点的值大的时候,我们就需要将父结点和子结点进行交换,然后再判断父结点是否小于它的父节点重复上述过程。具体详情可以看https://blog.csdn.net/m0_63267251/article/details/139388919这篇博客。

2.结点下移

当我们结点值修改后比它的左孩子或者右孩子值大,然后我们从中找到值最小的那个索引,然后让父结点跟最小的结点进行交换,交换后我们再判断当前结点的值是否比它的左孩子或者右孩子值大,重复上述操作,我们要保证父节点的值要小于它的左孩子和右孩子的值。 具体详情可以看https://blog.csdn.net/m0_63267251/article/details/139388919这篇博客。

    //传入结点下标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.插入一个数

插入一个数,我们直接在数组的末尾添加元素即可,而size既可以表示堆中元素的个数,也可以表示堆中元素在数组中存储的最后一个结点的下标。故我们添加元素为heap[size] = x;size++;

             if (cmd.equals("I")){x = Integer.parseInt(s[1]);heap[++size] = x;}

 4.输出当前集合的最小值

因为我们搭建的是小根堆,故我们堆中的根结点的值就是堆中元素的最小值即heap[1];

5.删除当前集合的最小值(数据保证此时的最小值唯一)

我们删除当前集合的最小值,数组的第一个元素是很难被删除的,我们还是删除数组的最后一个元素,将根节点赋值为数组的最后一个元素即heap[1] = heap[size];size--;然后此时有可能小根堆的性质被破坏,所以我们只需要将根节点结点下移操作就可以即down(1);此时我们就完成了删除当前集合最小值的情况。

6.删除第k个插入的数

这个操作麻烦的是删除第k个插入的数,而不是删除第k个结点。因此我们需要一个一维整型数组khp,来存储第k个插入的数在堆中的下标即khp[k];我们还需要一个一维整型数组hp,来存储我们堆中的点是第几个插入的点;例如kph[j] = k表示第j个插入的点是堆中第k个结点,ph[k] = j表示堆中的第k个结点时我们第k个插入的点,这两个数组的值对应的是相反的。那么我们就需要更新一下我们的down操作和up操作。

    public static void down(int x){int temp = x;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){hp_swap(temp,x);down(temp);}}//结点上移,只需要跟父结点比较即可public static void up(int x){while ( x / 2 >= 1 && heap[x / 2] > heap[x]){hp_swap(x,x/2);x /= 2;}}//因为hp数组可以对应为堆中的元素下标在khp数中的关系,所以我们先交换khp,后交换hp或者先交换hp再交换khp也可以public static void hp_swap(int a,int b){int t = khp[hp[a]];khp[hp[a]] = khp[hp[b]];khp[hp[b]] = t;t = hp[a];hp[a] = hp[b];hp[b] = t;t = heap[a];heap[a] = heap[b];heap[b] = t;}

 对应的插入一个数、输出当前集合和最小值和删除集合最小值操作更新。

            if (cmd.equals("I")){x = Integer.parseInt(s[1]);size++;m++;heap[size] = x;khp[m] = size;hp[size] = m;up(size);} else if (cmd.equals("PM")) {pw.println(heap[1]);} else if (cmd.equals("DM")) {hp_swap(1,size);size--;down(1);} else if (cmd.equals("D")) {k = Integer.parseInt(s[1]);//找到第k歌数在堆中的位置k = khp[k];hp_swap(k,size);size--;//两个操作最多执行一个up(k);down(k);}

7.修改第k个插入的数,修改为x

我们将插入第k个数在堆中的坐标找到即khp[k],然后将堆中的值修改为x即heap[khp[k]] = x。然后再维护一下小根堆性质down(kph[k])、up(khp[k])即可。

            if (cmd.equals("C")) {k = Integer.parseInt(s[1]);x = Integer.parseInt(s[2]);k = khp[k];heap[k] = x;down(k);up(k);}

三、代码如下

1.代码如下:


import java.util.*;
import java.io.*;
public class 模拟堆 {static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));static StreamTokenizer st = new StreamTokenizer(br);static int N = 100010;//堆中的数据static int[] heap = new int[N];static int size = 0;//第k个插入数在堆中的下标static int[] khp = new int[N];//堆中的哪一个点是我们第几个插入的点static int[] hp = new int[N];public static void main(String[] args) throws Exception{Scanner sc = new Scanner(br);int n = nextInt();//表示第几个插入的数int m = 0;while (n-- > 0){String[] s = nextLine().split(" ");String cmd = s[0];int x;int k;if (cmd.equals("I")){x = Integer.parseInt(s[1]);size++;m++;heap[size] = x;khp[m] = size;hp[size] = m;up(size);} else if (cmd.equals("PM")) {pw.println(heap[1]);} else if (cmd.equals("DM")) {hp_swap(1,size);size--;down(1);} else if (cmd.equals("D")) {k = Integer.parseInt(s[1]);//找到第k歌数在堆中的位置k = khp[k];hp_swap(k,size);size--;//两个操作最多执行一个up(k);down(k);} else if (cmd.equals("C")) {k = Integer.parseInt(s[1]);x = Integer.parseInt(s[2]);k = khp[k];heap[k] = x;down(k);up(k);}}pw.flush();}public static void down(int x){int temp = x;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){hp_swap(temp,x);down(temp);}}//结点上移,只需要跟父结点比较即可public static void up(int x){while ( x / 2 >= 1 && heap[x / 2] > heap[x]){hp_swap(x,x/2);x /= 2;}}//因为hp数组可以对应为堆中的元素下标在khp数中的关系,所以我们要先交换khp,后交换hp或者先交换hp再交换khp也可以public static void hp_swap(int a,int b){int t = khp[hp[a]];khp[hp[a]] = khp[hp[b]];khp[hp[b]] = t;t = hp[a];hp[a] = hp[b];hp[b] = t;t = heap[a];heap[a] = heap[b];heap[b] = t;}public static int nextInt()throws Exception{st.nextToken();return (int)st.nval;}public static String nextLine()throws Exception{return br.readLine();}
}

2.读入数据:

8
I -10
PM
I -10
D 1
C 2 8
I 6
PM
DM

3.代码运行结果

-10
6

总结

上述解释了一下模拟堆中的一些基本操作知道题中各个数组的意思,比较长和难以理解,可以多看看梳理一下。

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

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

相关文章

Springboot健身房管理系统-计算机毕业设计源码44394

摘 要 大数据时代下&#xff0c;数据呈爆炸式地增长。为了迎合信息化时代的潮流和信息化安全的要求&#xff0c;利用互联网服务于其他行业&#xff0c;促进生产&#xff0c;已经是成为一种势不可挡的趋势。在健身房管理的要求下&#xff0c;开发一款整体式结构的健身房管理系统…

代理 模式

一、什么是代理模式 代理模式指代理控制对其他对象的访问&#xff0c;也就是代理对象控制对原对象的引⽤。在某些情况下&#xff0c;⼀个对象不适合或者不能直接被引⽤访问&#xff0c;⽽代理对象可以在客⼾端和⽬标对象之间起到中介的作⽤。 二、为什么使用代理模式 模式作…

HW面试常见知识点2——研判分析(蓝队中级版)

&#x1f340;文章简介&#xff1a;又到了一年一度的HW时刻&#xff0c;本文写给新手想快速进阶HW蓝中的网安爱好者们&#xff0c; 通读熟练掌握本文面试定个蓝中还是没问题的&#xff01;大家也要灵活随机应变&#xff0c;不要太刻板的回答&#xff09; &#x1f341;个人主页…

揭秘下载数据背后的秘密,Xinstall助你掌握市场脉搏

在当下这个移动互联网时代&#xff0c;应用推广已成为各大企业竞争的重要战场。然而&#xff0c;如何有效地获取并分析应用下载数据&#xff0c;却成为了许多推广者面临的难题。今天&#xff0c;我们将为大家介绍一款强大的应用推广助手——Xinstall&#xff0c;它能够帮助你轻…

隐藏 IP 地址的重要性是什么?

在当今的数字时代&#xff0c;保护我们的在线身份至关重要。从保护个人信息到保护隐私&#xff0c;互联网用户越来越多地寻求增强在线安全性的方法。保持匿名和保护敏感数据的一个关键方面是隐藏您的 IP 地址。在这篇博文中&#xff0c;我们将深入探讨隐藏 IP 地址的重要性&…

人脸识别技术与人证合一智能闸机的剖析

人脸识别技术&#xff0c;作为一种先进的生物认证手段&#xff0c;依据个体面部独有的特征信息来进行身份验证。这项技术通过捕获图像或视频中的面部数据&#xff0c;执行一系列精密步骤&#xff0c;包括图像获取、面部定位、预处理、特征提取与比对&#xff0c;以确认个人身份…

【JMeter接口自动化】第2讲 Jmeter目录结构

JMeter的目录结构如下&#xff1a; bin目录&#xff1a;可执行文件目录&#xff0c;启动jmeter时&#xff0c;就是启动bin目录下的ApacheJmeter.jar&#xff0c;jmeter.bat&#xff0c;jmeter.sh ApacheJmeter.jar:启动文件 jmeter.bat&#xff1a;Windows 的启动命令。 jmeter…

前端框架前置知识之Node.js:fs模块、path模块、http模块、端口号介绍

什么是模块&#xff1f; 类似插件&#xff0c;封装了方法 / 属性 fs 模块- 读写文件 代码示例 // 1. 加载 fs 模块对象 const fs require(fs) // 2. 写入文件内容 fs.writeFile(./test.txt, hello, Node.js, (err) > {if (err) console.log(err) //若 err不为空&#xf…

韩顺平0基础学java——第15天

p303-326 重写override 和重载做个对比 注&#xff1a;但子类可以扩大范围&#xff0c;比如父类是protected&#xff0c;子类可以是public 多态 方法或对象具有多种形态&#xff0c;是面向对象的第三大特征&#xff0c;多态是建立在封装和继承基础之上的。 多态的具体体现…

绕过WAF(Web应用程序防火墙)--介绍、主要功能、部署模式、分类及注入绕过方式等

网站WAF是一款集网站内容安全防护、网站资源保护及网站流量保护功能为一体的服务器工具。功能涵盖了网马/木马扫描、防SQL注入、防盗链、防CC攻击、网站流量实时监控、网站CPU监控、下载线程保护、IP黑白名单管理、网页防篡改功能等模块。能够为用户提供实时的网站安全防护&…

Java开发:Spring Boot 实战教程

序言 随着技术的快速发展和数字化转型的深入推进&#xff0c;软件开发领域迎来了前所未有的变革。在众多开发框架中&#xff0c;Spring Boot凭借其“约定大于配置”的核心理念和快速开发的能力&#xff0c;迅速崭露头角&#xff0c;成为当今企业级应用开发的首选框架之一。 《…

git拉去代码报错“Failed to connect to 127.0.0.1 port 31181: Connection refused“

最近参与了一个新项目&#xff0c;在使用git clone 克隆代码时遇到了一个报错"fatal: unable to access ‘https://example.git/’: Failed to connect to 127.0.0.1 port 31181: Connection refused",今天就和大家分享下解决过程。 报错详情 在使用git clone 克隆…

【JavaEE】Servlet

文章目录 一、Servlet 是什么二、如何创建Servlet程序1、创建项目2、引入依赖3、创建目录4、编写代码5、打包程序6、部署程序7、验证程序 一、Servlet 是什么 二、如何创建Servlet程序 1、创建项目 2、引入依赖 Maven 项目创建完后&#xff0c;会自动生成一个 pom.xml 的文…

coze自定义插件调用3

1&#xff0c;打开我的空间&#xff1b; 2&#xff0c;编辑&#xff0c;选择快捷指令 3&#xff0c;编辑指令 4&#xff0c;实际测试【输入框多了一个按钮“查询基础信息”&#xff0c;点击查询基础信息&#xff0c;提示输入缴费卡号&#xff0c;提交后如下图】

HTTP --tcp和keep-alive

TCP TCP连接 tcp/ip是全球计算机以及网络设备都在使用的一种常见的分组交换网络分层协议集&#xff0c;客户端可以打开一条tcp/ip连接&#xff0c;连接到可能运行在世界各地的服务器应用程序&#xff0c;一旦连接建立起来了&#xff0c;在客户端和服务器的计算机之间交换的报…

ar地产沙盘互动体验提供更加丰富多彩的楼盘信息

AR增强现实技术作为其重要分支&#xff0c;正逐步在全球市场中崭露头角。国内的AR增强现实技术公司正致力于链接物理世界和虚拟世界&#xff0c;为用户带来沉浸式的AR体验。它们打造线上线下联动的一站式文旅景区数字化运营平台&#xff0c;让您在享受旅游的同时&#xff0c;也…

用容器构建wordpress项目

用容器构建wordpress项目 #准备两个镜像 #数据库和centos docker pull mysql:5.7 docker pull centos:7 #创建一个wordpress文件夹&#xff0c;在wordpress文件里面写一个Dockerfile文件 vim DockerfileFROM centos:7 #基于centos环境RUN yum -y install epel-release ;\ #安装…

vue3状态管理,pinia的使用

​​​​​​​状态管理 我们知道组件与组件之间可以传递信息&#xff0c;那么我们就可以将一个信息作为组件的独立状态&#xff08;例如&#xff0c;单个组件的颜色&#xff09;或者共有状态&#xff08;例如&#xff0c;多个组件是否显示&#xff09;在组件之传递&#xff0c…

顺序表的讲解与实现

顺序表的讲解与实现 一、顺序表的概念及结构二、顺序表分类顺序表和数组的区别顺序表分类静态顺序表动态顺序表 三、动态顺序表的实现(使用VS2022)1.初始化、销毁、打印内容2.检查扩容3.尾部插入、尾部删除、头部插入、头部删除尾部插入尾部删除头部插入头部删除 4.指定插入、指…

C++ stack类与queue类

目录 0.前言 1.容器适配器 1.1容器适配器的特点 1.2容器适配器的实现 1.3使用容器适配器的场景 2.stack的介绍与使用 2.1介绍 2.2使用 3.queue的介绍与使用 3.1介绍 3.2使用 4.stack和queue的模拟实现 4.1 stack的模拟实现 4.2 queue的模拟实现 5.结语 &#xf…