Java数据结构-堆和优先级队列

目录

  • 1. 相关概念
  • 2. PriorityQueue的实现
    • 2.0 搭建整体框架
    • 2.1 堆的创建和调整
    • 2.2 插入元素
    • 2.3 出堆顶元素
  • 3. 全部代码(包含大根堆和小根堆)
  • 4. PriorityQueue的使用
  • 5. Top-K问题

之前我们学习的二叉树的存储方式是链式存储,(不清楚的可以看这篇哦: 二叉树),而堆是二叉树的另一种存储方式:顺序存储,jdk1.8中的优先级队列: PriorityQueue 的底层就是使用了堆这种数据结构

1. 相关概念

堆,可以认为是一棵使用顺序存储的方式来存储数据的一棵完全二叉树,堆分为大根堆和小根堆
大根堆:每个结点的值都不小于左右孩子结点的值
小根堆:每个结点的值都不大于左右孩子结点的值
如图:
在这里插入图片描述

复习:
如果父亲结点是i下标:左孩子2i+1;右孩子2i+2
如果孩子结点是i下标:父亲( i-1)/ 2(不管i是左孩子还是右孩子)

2. PriorityQueue的实现

PriorityQueue默认是小根堆,所以我们也实现一个小根堆,文章最后会给出大根堆和小根堆的全部代码哦

2.0 搭建整体框架

定义一个Heap类

public class Heap {public int[] elem;//存储数据的数组public int curSize;//当前数据的个数
}

2.1 堆的创建和调整

当拿到一组数据后如:2、4、3、9、6、1、5,如何将这组数据调整为小根堆?先将数据按层序遍历的方式,画出一棵二叉树,如图:

从最后一棵子树开始调整,循环直到整棵树都是小根堆

    //创建堆(小根堆)public void createHeap() {for (int parent = (elem.length - 1 - 1) / 2; parent >= 0; parent--) {shiftDown(elem, parent, elem.length);}}
    //向下调整的逻辑:private void shiftDown(int[] array, int parent, int end) {int child = (parent * 2) + 1;while (child < end) {if (child + 1 < end && array[child] > array[child + 1]) {child++;}//保证child下标是最小的if (array[child] < array[parent]) {//交换child下标和parent下标的值int tmp = array[child];array[child] = array[parent];array[parent] = tmp;//parent = child;child = parent * 2 + 1;} else {break;//已经是小根堆了,结束循环}}}

如何调整?拿parent=0举例
如图:调整
在这里插入图片描述
以下是进行一次调整的逻辑,对每棵子树都进行向下调整,整棵树就能变成小根堆
在这里插入图片描述

2.2 插入元素

插入是在数组的最后一个元素之后插入,插入之后的堆中的最后一个元素定义为child,child和它的父亲比较,将较小的做为根节点,接着
在这里插入图片描述

    public void offer(int key) {if (curSize == elem.length) {//扩容elem = Arrays.copyOf(elem, elem.length * 2);}elem[curSize] = key;curSize++;shiftUp(curSize - 1);}
//向上调整public void shiftUp(int child) {int parent = (child - 1) / 2;while (parent >= 0) {if (elem[child] > elem[parent]) {//交换int tmp = elem[child];elem[child] = elem[parent];elem[parent] = tmp;child = parent;parent = (child - 1) / 2;} else {break;}}}

2.3 出堆顶元素

将堆顶元素和最后一个元素交换,前有效数据个数-1,接着将删除后的堆进行向上调整

    public int pool() {int ret = elem[0];swap(elem, 0, curSize - 1);curSize--;shiftDown(elem, 0, curSize);return ret;}

3. 全部代码(包含大根堆和小根堆)

public class Heap {public int[] elem;//存储数据的数组public int curSize;//当前数据的个数public Heap(int[] array) {//构造方法,初始化时给elem数组elem = new int[10];for (int i = 0; i < array.length; i++) {elem[i] = array[i];curSize++;}}//创建堆(大根堆)public void createMaxHeap(int[] array) {for (int parent = (array.length - 1 - 1) / 2; parent >= 0; parent--) {shiftDownMax(elem, parent, array.length);}}//创建堆(小根堆)public void createMinHeap(int[] array) {for (int parent = (elem.length - 1 - 1) / 2; parent >= 0; parent--) {shiftDownMin(elem, parent, elem.length);}}//向下调整:大根堆private void shiftDownMax(int[] array, int parent, int end) {int child = (parent * 2) + 1;while (child < end) {if (child + 1 < end && array[child] < array[child + 1]) {child++;}//child是最大的if (array[child] > array[parent]) {swap(array, child, parent);parent = child;child = parent * 2 + 1;} else {break;//已经是大根堆了,结束循环}}}//向下调整:小根堆private void shiftDownMin(int[] array, int parent, int end) {int child = (parent * 2) + 1;while (child < end) {if (child + 1 < end && array[child] > array[child + 1]) {child++;}//child是最大的if (array[child] < array[parent]) {swap(array, child, parent);parent = child;child = parent * 2 + 1;} else {break;//已经是小根堆了,结束循环}}}/*** 插入数据,大根堆** @param key*/public void offerMax(int key) {if (curSize == elem.length) {//扩容elem = Arrays.copyOf(elem, elem.length * 2);}elem[curSize] = key;curSize++;shiftUpMax(curSize - 1);}/*** 插入数据,小根堆** @param key*/public void offerMin(int key) {if (curSize == elem.length) {//扩容elem = Arrays.copyOf(elem, elem.length * 2);}elem[curSize] = key;curSize++;shiftUpMin(curSize - 1);}/*** 删除数据,大根堆** @return*/public int poolMax() {if (isEmpty()) {return -1;}int ret = elem[0];swap(elem, 0, curSize - 1);curSize--;shiftDownMax(elem, 0, curSize);return ret;}/*** 删除数据,小根堆* @return*/public int poolMin() {if (isEmpty()) {return -1;}int ret = elem[0];swap(elem, 0, curSize - 1);curSize--;shiftDownMin(elem, 0, curSize);return ret;}//向上调整,大顶堆private void shiftUpMax(int child) {int parent = (child - 1) / 2;while (parent >= 0) {if (elem[child] > elem[parent]) {swap(elem, parent, child);child = parent;parent = (child - 1) / 2;} else {break;}}}//向上调整,大顶堆private void shiftUpMin(int child) {int parent = (child - 1) / 2;while (parent >= 0) {if (elem[child] < elem[parent]) {swap(elem, parent, child);child = parent;parent = (child - 1) / 2;} else {break;}}}//交换private void swap(int[] arr, int x, int y) {int tmp = arr[x];arr[x] = arr[y];arr[y] = tmp;}@Overridepublic String toString() {StringBuilder str = new StringBuilder();for (int i = 0; i < curSize; i++) {str.append(elem[i]);if (i != curSize - 1) {str.append(", ");}}return str.toString();}//判断是否为空private boolean isEmpty() {return curSize == 0;}
}

4. PriorityQueue的使用

PriorityQueue的插入、删除的方法名和我们实现的一样这里不多赘述,Java中的PriorityQueue默认是小根堆,如果想变成大根堆,需要在实例化时传入自己实现的比较器

class Com implements Comparator<Integer> {@Overridepublic int compare(Integer o1, Integer o2) {return o2 - o1;//大堆}
}
public static void main(String[] args) {PriorityQueue<Integer> queue = new PriorityQueue<>(new Com());queue.offer(1);
}

今天的内容就到这里~感谢大家的支持!

5. Top-K问题

Top-K问题是求一个数据集合中,前K个最大值或者最小值,例如班级排名前十、世界五百强等。我们很容易想到将数据进行排序,但是当数据量比较大时,排序的效率很低,而且并不能将数据全部加载到内存中,那么怎么解决这个问题?最好的办法就是使用堆。
求前K个最大的元素: 建一个大小为K的小堆,剩下的N-K个元素与堆顶比较,将不符合要求的元素替换掉
求前K个最小的元素: 建一个大小为K的大堆,剩下的N-K个元素与堆顶比较,将不符合要求的元素替换掉
例题:最小K个数

class intCmp implements Comparator<Integer> {@Overridepublic int compare(Integer o1, Integer o2) {return o2.compareTo(o1);}
}class Solution {public int[] smallestK(int[] arr, int k) {PriorityQueue<Integer> queue = new PriorityQueue<>(new intCmp());int[] ret = new int[k];// 要返回的数组if (k == 0) {return ret;}// 建大小为k的大根堆for (int i = 0; i < k; i++) {queue.offer(arr[i]);}for (int i = k; i < arr.length; i++) {int val = queue.peek();if (val > arr[i]) {queue.poll();queue.offer(arr[i]);}}for (int i = 0; i < k; i++) {ret[i] = queue.poll();}return ret;}
}

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

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

相关文章

java的各种锁

我们先来看看有什么锁 一、java锁 1、乐观锁 乐观锁 是一种乐观思想 &#xff0c;假定当前环境是读多写少&#xff0c;遇到并发写的概率比较低&#xff0c;读数 据时认为别的线程不会正在进行修改&#xff08;所以没有上锁&#xff09;。写数据时&#xff0c;判断当前 与期望…

算法打卡day48|动态规划篇16| Leetcode 583. 两个字符串的删除操作、72. 编辑距离

算法题 Leetcode 583. 两个字符串的删除操作 题目链接:583. 两个字符串的删除操作 大佬视频讲解&#xff1a;583. 两个字符串的删除操作视频讲解 个人思路 本题和115.不同的子序列相比&#xff0c;变为了两个字符串都可以删除&#xff0c;整体思路是不变的&#xff0c;依旧…

vue3中web前端JS动画案例(二)多物体运动-多值运动

<script setup> import { ref, onMounted, watch } from vue // ----------------------- 01 js 动画介绍--------------------- // 1、匀速运动 // 2、缓动运动&#xff08;常见&#xff09; // 3、透明度运动 // 4、多物体运动 // 5、多值动画// 6、自己的动画框架 // …

在PostgreSQL中如何实现递归查询,例如使用WITH RECURSIVE构建层次结构数据?

文章目录 解决方案使用WITH RECURSIVE进行递归查询示例代码 总结 在PostgreSQL中&#xff0c;递归查询是一种非常强大的工具&#xff0c;它可以用来查询具有层次结构或树形结构的数据。例如&#xff0c;你可能会在员工-经理关系、目录结构或组织结构图中遇到这样的数据。为了处…

MybatisPlus 逻辑删除

目录 一、配置MybatisPlus 二、添加注解 三、调用MybatisPlus的删除方法 四、测试结果 一、配置MybatisPlus # mybatis-plus mybatis-plus:# 全局配置global-config:db-config:# 全局逻辑删除的字段名logic-delete-field: deleted# 逻辑已删除值(默认为 d)logic-delete-va…

为什么科拓停车选择OceanBase来构建智慧停车SaaS应用

本文来自OceanBase的客户——拓客停车的实践分享 科拓停车简介与业务背景 作为智慧停车行业的佼佼者&#xff0c;科拓停车致力于提供全方位的智慧停车解决方案。服务涵盖车场运营管理、互联网智慧停车平台以及停车场增值服务等。通过不断研发创新&#xff0c;打造出了多样化的…

Linux的主机状态

查看系统资源占用 可以通过top命令查看CPU、内存使用情况&#xff0c;类似Windows的任务管理器 默认每5秒刷新一次&#xff0c;语法&#xff1a;直接输入top即可&#xff0c;按q或ctrl c退出 第一行&#xff1a; top&#xff1a;命令名称&#xff0c;14:39:58&#xf…

Sentinel 流控注解使用

大概原理&#xff1a;通过反射解析注解 SentinelResource信息完成调用&#xff0c;处理方法&#xff0c;类似AOP编程 处理方法的返回类型要保持一致&#xff0c;参数和顺序保持一致&#xff0c; 可以在参数列表最后加 com.alibaba.csp.sentinel.slots.block.BlockException; …

什么是时间序列分析

时间序列分析是现代计量经济学的重要内容&#xff0c;广泛应用于经济、商业、社会问题研究中&#xff0c;在指标预测中具有重要地位&#xff0c;是研究统计指标动态特征和周期特征及相关关系的重要方法。 一、基本概念 经济社会现象随着时间的推移留下运行轨迹&#xff0c;按…

现代农业AI智能化升级之路:机器学习在现代农业领域的现状与未来发展

&#x1f9d1; 作者简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

学习笔记-数据结构-线性表(2024-04-18)- 单向链表选择排序

试以单向链表为存储结构实现简单选择排序的算法。 实现递增排序&#xff0c;首先选择一个元素作为第一个比较值&#xff0c;遍历其他所有的元素&#xff0c;如果发现其他元素中有比它小的元素&#xff0c;则交换两个元素&#xff0c;这样每一趟都能找到符合要求的最小值 正经…

展开说说:Android Fragment完全解析-卷一

1、是什么 Fragment 中文意思是碎片&#xff0c;Android 3.0推出的一个系统组件&#xff0c;主打一个在应用界面中可模块化又可重复使用。 Fragment 它很独立&#xff0c;它可以定义和管理自己的布局&#xff0c;具有自己的生命周期&#xff0c;并且可以处理自己的输入事件。…

4.18学习总结

多线程补充 等待唤醒机制 现在有两条线程在运行&#xff0c;其中一条线程可以创造一个特殊的数据供另一条线程使用&#xff0c;但这个数据的创建也有要求&#xff1a;在同一时间只允许有一个这样的特殊数据&#xff0c;那么我们要怎样去完成呢&#xff1f;如果用普通的多线程…

解决Error in writing header file of the driver

在源代码里面更新了一批常规的内容&#xff0c;编译的时候遇到一个error&#xff0c;一大片都是红的。XXX是项目名称。 Description Resource Path Location Type Generator: ERROR: Error in writing header file of the driver XXX Cpu Processor Expert Problem 表面意思是…

【学习】Jmeter、postman、python如何与数据库相互配合

在当今数字化时代&#xff0c;数据库已经成为我们日常生活中不可或缺的一部分。无论是购物、社交还是工作&#xff0c;数据库都在默默地为我们提供着高效、稳定的服务。而在众多的技术工具中&#xff0c;Jmeter、Postman和Python成为了操作数据库的三大主流技术。今天&#xff…

【嵌入式DIY实例】-指纹锁

DIY指纹锁 文章目录 DIY指纹锁1、硬件准备1.1 R307指纹传感器模介绍2、硬件接线原理图3、代码实现在这个项目中,我们将使用 Arduino 构建一个指纹门锁安全系统。 该系统可用于我们的家庭、办公室等提供安全保障。 我们还可以用它来打开门,只需将手指放在门锁上即可。 安全是许…

Linux管道共享内存

前言 进程虽然是独立运行的个体&#xff0c;但它们之间有时候需要协作才能完成一项工作&#xff0c;比如有两个进程需要同步数据&#xff0c;进程 A 把数据准备好后&#xff0c;想把数据发往进程 B&#xff0c;进程 B 必须被提前通知有数据即将到来&#xff0c;或者进程 A 想发…

项目七:学会使用python爬虫解析库(小白大成级)

前期我们学会了怎么使用python爬虫请求库和解析库的简单应用和了解&#xff0c;同时能够对爬虫有一个较为清晰的体系&#xff0c;毕竟简单的爬虫基本上都是请求数据——解析数据——存储数据的大概流程。 那么回忆一下&#xff0c;请求库我们学的是requests模块&#xff0c;解…

项目管理-项目开发计划介绍

目录 一、内容总览 二、项目开发计划概述 2.1 概述 2.2 项自开发计划的目的和作用 2.3 项目开发计划的内容 2.3.1 工作计划 2.3.2 人员组织计划 2.3.3 设备采购和资源供应计划 2.3.4 配置管理计划 2.3.5 进度安排计划 2.3.6 成本投资计划 2.3.7 质量保证计划 2.3.8…

实现I.MX6ULL开发板与Windows和Ubuntu系统之间的通信

虚拟机与主机之间的连接方式确实包括桥接模式、NAT模式和仅主机模式。 桥接模式&#xff08;Bridged&#xff09;&#xff1a;在桥接模式下&#xff0c;虚拟机通过虚拟交换机直接连接到主机的物理网络上&#xff0c;就像一台独立的物理机器一样&#xff0c;拥有自己的IP地址&a…