Java学习苦旅(十九)——详解Java的堆和优先级队列

本篇博客将详细讲解堆和优先级队列。

文章目录

    • 概念
    • 向下调整
  • 优先级队列
    • 概念
    • 内部原理
    • 入队列
    • 出队列
    • 返回队首元素
    • java中的优先级队列
      • 常用操作
  • topK问题
  • 结尾

概念

  1. 堆逻辑上是一棵完全二叉树。

  2. 堆物理上是保存在数组中。

  3. 满足任意结点的值都大于其子树中结点的值,叫做大堆,或者大根堆,或者最大堆。反之,则是小堆,或者小根堆,或者最小堆。

image-20220306165626174

image-20220306170006428

堆的基本作用就是快速找出集合中的最值。

向下调整

**前提:**左右子树必须已经是一个堆,才能调整。

例如:

image-20220306181206804

示例代码:

public class TestHeap {
    public int[] elem;
    public int usedSize;

    public TestHeap() {
        this.elem = new int[10];
    }

    /**
     * 向下调整函数的实现
     * @param parent 每棵树的根节点
     * @param len 每棵树调整的结束位置
     */
    public void shiftDown(int parent, int len) {
        int child = 2*parent + 1;
        while (child < len) {
            if (child+1 < len && elem[child] < elem[child+1]) {
                child++;
            }
            if (elem[child] > elem[parent]) {
                int tmp = elem[child];
                elem[child] = elem[parent];
                elem[parent] = tmp;
                parent = child;
                child = 2*parent + 1;
            } else {
                break;
            }
        }
    }

    public void createHeap(int[] array) {
        for (int i = 0; i < array.length; i++) {
            elem[i] = array[i];
            usedSize++;
        }
        for (int parent = (usedSize-1-1)/2; parent >= 0; parent--) {
            shiftDown(parent,usedSize);
        }
    }
}

此时,向下调整的代码时间复杂度为O(N)

优先级队列

概念

在很多应用中,我们通常需要按照优先级情况对待处理对象进行处理,比如首先处理优先级最高的对象,然后处理次高的对象。最简单的一个例子就是,在手机上玩游戏的时候,如果有来电,那么系统应该优先处理打进来的电话。在这种情况下,我们的数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象。这种数据结构就是优先级队列(Priority Queue)。

内部原理

优先级队列的实现方式有很多,但最常见的是使用堆来构建。

入队列

过程(以大根堆为例):

  1. 首先按尾插方式放入数组

  2. 比较其和其双亲的值的大小,如果双亲的值大,则满足堆的性质,插入结束

  3. 否则,交换其和双亲位置的值,重新进行 2、3 步骤

  4. 直到根结点

示例代码

public void shiftUp(int child) {
    int parent = (child-1)/2;
    while (child > 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;
        }
    }
}

public void offer(int val) {
    if (isFull()) {
        elem = Arrays.copyOf(elem,2*elem.length);
    }
    elem[usedSize++] = val;
    shiftUp(usedSize-1);
}

public boolean isFull() {
    return usedSize == elem.length;
}

出队列

为了防止破坏堆的结构,删除时并不是直接将堆顶元素删除,而是用数组的最后一个元素替换堆顶元素,然后通过向下调整方式重新调整成堆。

示例代码

public int poll() {
    if (isEmpty()) {
        throw new RuntimeException("优先级队列为空");
    }
    int tmp = elem[0];
    elem[0] = elem[usedSize-1];
    elem[usedSize-1] = tmp;
    usedSize--;
    shiftDown(0,usedSize);
    return tmp;
}

public boolean isEmpty() {
    return usedSize == 0;
}

返回队首元素

返回堆顶元素即可

示例代码

public int peek() {
    if (isEmpty()) {
        throw new RuntimeException("优先级队列为空");
    }
    return elem[0];
}

java中的优先级队列

PriorityQueue代表java中的优先级队列。

常用操作

错误处理抛出异常返回特殊值
入队列add(e)offer(e)
出队列remove()poll()
队首元素element()peek()

PriorityQueue默认是小根堆。

topK问题

topK问题是指给定一组数据,找出前K个最大或最小的元素。

我们可以使用优先级队列去解决这个问题。

  • 如果求前K个最大元素,建一个小根堆。
  • 如果求前K个最小元素,建一个大根堆。
  • 如果求第K大的元素,建一个小根堆,堆顶元素就是第K大的元素。
  • 如果求第K小的元素,建一个大根堆,堆顶元素就是第K小的元素。

假如求前K个最小元素,具体代码如下:

import java.util.Arrays;
import java.util.Comparator;
import java.util.PriorityQueue;

public static int[] topK(int[] array, int k) {
    //创建一个大小为K的大根堆
    PriorityQueue<Integer> maxHeap = new PriorityQueue<>(k, new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2-o1;
        }
    });
    //遍历数组中的元素,前K个元素放到队列中
    for (int i = 0; i < array.length; i++) {
        if (maxHeap.size() < k) {
            maxHeap.offer(array[i]);
        } else {
            //从第K+1个元素开始,每个元素和堆顶元素比较
            int top = maxHeap.peek();
            if (top > array[i]) {
                maxHeap.poll();
                maxHeap.offer(array[i]);
            }
        }
    }
    int[] tmp = new int[k];
    for (int i = 0; i < k; i++) {
        tmp[i] = maxHeap.poll();
    }
    return tmp;
}

我们可以验证一下,输入:

int[] array = {18,21,8,10,34,12};
int[] tmp = topK(array,3);
System.out.println(Arrays.toString(tmp));

输出:

image-20220309185801522

结尾

本篇博客到此结束。
上一篇博客:Java学习苦旅(十八)——详解Java中的二叉树
下一篇博客预告:Java学习苦旅(二十)——七大排序(JAVA代码)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

北京大学漏洞报送证书

获取来源&#xff1a;edusrc&#xff08;教育漏洞报告平台&#xff09; url&#xff1a;教育漏洞报告平台(EDUSRC) 兑换价格&#xff1a;30金币 获取条件&#xff1a;北京大学任意中危或以上级别漏洞

【React系列】Portals、Fragment

本文来自#React系列教程&#xff1a;https://mp.weixin.qq.com/mp/appmsgalbum?__bizMzg5MDAzNzkwNA&actiongetalbum&album_id1566025152667107329) Portals 某些情况下&#xff0c;我们希望渲染的内容独立于父组件&#xff0c;甚至是独立于当前挂载到的DOM元素中&am…

浅谈基于物联网的建筑物综合环境能耗监测管理系统

叶根胜 安科瑞电气股份有限公司 上海嘉定 201801 摘要&#xff1a;随着社会经济的快速发展&#xff0c;我国建筑能源消费总量逐年增加&#xff0c;占社会能源消费总量的近30%。国际发达国家建设部科技司的相关研究表明&#xff0c;随着城市化进程的加快和人民生活质量的提高&…

案例091:基于微信小程序的农场驿站平台的设计与实现

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

ubuntu桥接方式上网

vmvare:VMware Workstation 17 Pro ubuntu: Ubuntu 14.04.6 LTS window10 下面是我的电脑配置 下面是ubuntu虚拟机的配置 vi /etc/network/interfaces 下面的gateway就是window -ipconfig 截图里的默认网关 auto lo iface lo inet loopbackauto eth0 iface eth0 inet stat…

日常工作 经验总结

1,在使用vue2开发项目时,快捷有效的组件化component 若有参数传递时,可以通过这样传递 在component中: 2,上拉加载,下拉刷新 若是使用局部进行上拉加载 下拉刷新 且需要用到scroll-view时 那么需要切记scroll-view在内被mescroll-uni包裹。若场景有限 对于无数据显示…

优雅实现微信小程序动态tabBar,根据不同用户角色显示不同底部导航——更新版(支持自由组合总数超过5个tabBar菜单)

背景 在开发小程序过程中&#xff0c;有个需求是&#xff0c;小程序底部的tabBar需要根据不同用户角色显示不同底部导航。此时就需要用到自定义底部导航 custom-tab-bar。 上次发文是组合显示4个底部tabBar导航&#xff0c;很多小伙伴评论说组合超过5个怎么办。他们的需求总数…

C语言KR圣经笔记 5.6指针数组;指针的指针

5.6 指针数组&#xff1b;指针的指针 因为指针本身也是变量&#xff0c;所以它们也能像其他变量一样保存在数组里面。我们写个程序来说明&#xff0c;该程序将一些文本行按照字母顺序排列&#xff0c;算是 UNIX 程序 sort 的精简版本。 在第三章中&#xff0c;我们介绍了对一…

设计创新,流程优化:3D开发HOOPS在数字化工厂中的多面应用

随着科技的不断发展&#xff0c;数字化转型已经成为各行各业的共同趋势&#xff0c;而工业领域也不例外。在这一浩大的变革浪潮中&#xff0c;Tech Soft 3D的HOOPS正以其卓越的性能和多功能性&#xff0c;成为数字化工厂领域的关键推动力。 数字化工厂概述 数字化工厂是指通过…

ssl证书(https/wss)内网测试

前言 一般后端部署到外网&#xff0c;可以去申请免费的SSL 证书&#xff0c; 但在内网测试时&#xff0c;需要自己生成证书 本章主要讲述ssl证书生成 1:环境 生成证书 openssl &#xff08;windows or linux 都行) 2:生成证书 1>生成私钥 pkcs#1私钥 openssl genrsa -out…

uniCloud 云函数

相对于云函数&#xff0c;官方更推荐使用 云对象 新建云函数 编辑云函数 uniCloud-aliyun/cloudfunctions/hello_func/index.js use strict; exports.main async (event, context) > {let {name} eventreturn 你好&#xff0c;${name}! };云函数接收的参数从event中解构获…

Js的String的replace(和replaceAll(

EcmaJavascriptJs的String的 replace( 和 replaceAll( 方法 String.prototype.replaceString.prototype.replaceAll 相同点 都是String.prototype的函数都是用于字符串替换都是两个参数第一个参数都可以是正则或字符串第二参数都可以是字符串或者回调函数, 回调会传入一个参…

YOLOv8改进 | 主干篇 | EfficientNetV1均衡缩放网络改进特征提取层

一、本文介绍 这次给大家带来的改进机制是EfficientNetV1主干,用其替换我们YOLOv8的特征提取网络,其主要思想是通过均衡地缩放网络的深度、宽度和分辨率,以提高卷积神经网络的性能。这种方法采用了一个简单但有效的复合系数,统一调整所有维度。经过我的实验该主干网络确实…

Linux系统:进程和计划任务管理

目录 一、程序 二、进程 1、什么是进程 1.1 进程的概念 1.2 进程的特征 1.3 进程、线程和协程 2、进程状态 3、进程的类型 4、进程使用内存出现的问题 三、进程管理相关命令 1、ps&#xff08;process state&#xff09; 1.1 用法 1.2 分析ps命令输出的内容 2、t…

基于STM32的ESP8266 WiFi模块数据采集与显示

基于STM32的ESP8266 WiFi模块数据采集与显示是一种常见的嵌入式系统应用&#xff0c;通常用于远程数据监测和控制。在这种应用中&#xff0c;STM32作为主控制器负责采集周围环境的数据&#xff0c;通过ESP8266 WiFi模块将数据发送到远程服务器&#xff0c;并在远程服务器上进行…

前端跨域问题的解决思路

目录 前言 跨域问题的解决思路 一般跨域的解决方案 前言 做了一个简单页面&#xff0c;做了一些数据埋点&#xff0c;想通过企业微信机器人来推送数据&#xff0c;遇到了一些问题&#xff0c;顺便记录下。 跨域问题的解决思路 由于是项目比较简单&#xff0c;直接使用了aj…

vue中key的用法

加key是提升vue渲染效率&#xff0c;减少DOM操作。 vue列表元素的更新机制&#xff1a; 当列表元素没有设置key的时候&#xff0c;vue判断是否操作这个DOM元素&#xff0c;是根据新旧两次数据的元素顺序进行对比&#xff0c;看一下元素内容是否发生变化。发生变化vue就操作这个…

2024.1.4每日一题

LeetCode每日一题 2397.被列覆盖的最多行数 2397. 被列覆盖的最多行数 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给你一个下标从 0 开始、大小为 m x n 的二进制矩阵 matrix &#xff1b;另给你一个整数 numSelect&#xff0c;表示你必须从 matrix 中选择的 不同 …

Android Studio 报错Failed to find Build Tools revision 28.0.3

目录 前言 一、报错信息 二、报错原因 三、解决方案 四、更多资源 前言 当Android Studio报错提示"Failed to find Build Tools revision 28.0.3"时&#xff0c;通常意味着您的项目需要使用28.0.3版本的构建工具&#xff0c;但系统中并没有找到对应的版本。这可…

01第一个Mybatis程序+引入Junit+引入日志文件logback

Mybatis MyBatis本质上就是对JDBC的封装&#xff0c;通过MyBatis完成CRUD。而对于JDBC&#xff0c;SQL语句写死在Java程序中&#xff0c;不灵活。改SQL的话就要改Java代码。违背开闭原则OCP。对于事务机制&#xff0c;MyBatis支持 或managed模式&#xff0c;JDBC模式中MyBatis…