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;
    }

    @Override
    public 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> {
    @Override
    public 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> {

    @Override
    public 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.mfbz.cn/a/560707.html

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

相关文章

idm线程越多越好吗 idm线程数多少合适

IDM&#xff08;Internet Download Manager&#xff09;是一款流行的下载管理软件&#xff0c;它支持多线程下载&#xff0c;这意味着它可以同时建立多个连接来下载文件的不同部分&#xff0c;从而提高下载速度。我们在使用IDM的时候总是有很多疑问&#xff0c;今天我们学习IDM…

LeetCode刷题实战5:最长回文子串

题目内容 给你一个字符串 s&#xff0c;找到 s 中最长的回文子串。 如果字符串的反序与原始字符串相同&#xff0c;则该字符串称为回文字符串。 示例 1&#xff1a; 输入&#xff1a;s "babad" 输出&#xff1a;"bab" 解释&#xff1a;"aba"…

【刷题】 二分查找进阶

送给大家一句话&#xff1a; 你向神求助是因为相信神&#xff0c;神没有回应你是因为神相信你 ε≡٩(๑>₃<)۶ &#xfeff;ε≡٩(๑>₃<)۶ &#xfeff;ε≡٩(๑>₃<)۶ 一心向学 二分查找进阶 1 前言Leetcode 852. 山脉数组的峰顶索引题目描述算法思…

【C++提高】常用容器

常用容器 引言&#xff1a;迭代器的使用一、vector容器1. vector基本概念2. vector的迭代器3. vector构造函数4. vector赋值操作5. vector容量和大小6. vector插入和删除7. vector数据存取8. vector互换容器9. vector预留空间 二、deque容器1. deque容器的基本概念2. deque容器…

终端的颜值担当-WindTerm

一、序言 今天就不给各位大佬聊技术了&#xff0c;给大佬们分享一款高颜值的终端工具——WindTerm。 二、什么是 WindTerm WindTerm&#xff08;也称为 Wind Term&#xff09;是一款终端仿真器&#xff0c;可用于在 Windows/MacOS/Linux 操作系统上模拟类 Unix 环境的命令行…

Python爬虫使用需要注意什么?应用前景如何?

Python爬虫很多人都听说过&#xff0c;它是一种用于从网页上获取信息的程序&#xff0c;它可以自动浏览网页、提取数据并进行处理。技术在使用Python爬虫时需要注意一些重要的事项&#xff0c;同时本文也会跟大家介绍一下爬虫的应用前景。 第一个注意事项就是使用Python爬虫时…

基于注解配置bean

文章目录 1.基本使用1.基本介绍2.快速入门1.引入jar包2.MyComponent.java3.UserAction.java3.UserDao.java4.UserService.java5.beans05.xml6.断点查看bean对象是否创建7.测试 3.注意事项和细节 2.自己实现spring注解1.需求分析2.思路分析图3.编写自定义注解ComponentScan4.编写…

今日arXiv最热NLP大模型论文:面向不确定性感知的Language Agent

引言&#xff1a;面向不确定性的感知的Language Agent Language Agent利用大型语言模型&#xff08;如OpenAI发布的GPT系列、Meta的LLaMA2等&#xff09;来与外部世界互动&#xff0c;例如通过工具和API收集观察结果&#xff0c;并处理这些信息以解决任务。这些Language Agent…

javaWeb项目-智能仓储系统功能介绍

项目关键技术 开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端&#xff1a;Vue、ElementUI 关键技术&#xff1a;springboot、SSM、vue、MYSQL、MAVEN 数据库工具&#xff1a;Navicat、SQLyog 1、JSP技术 JSP(Jav…

UE5集成gRPC

最近有项目需要在UE5里做RPC&#xff0c;对比了thrift、gRPC、rcplib等开源rpc框架&#xff0c;由于习惯使用protobuf&#xff0c;故选择了gRPC。然而&#xff0c;Google出品也是一言难尽啊&#xff0c;最起码编译太繁琐了。 本次使用的gRPC版本为1.62.1&#xff0c;UE5.2&…

二分答案复习

y总二分查找算法模板 int bsearch_1(int l, int r) {while (l < r){int mid l r >> 1;//性质在右边&#xff0c;区间划分成[l, mid]和[mid 1, r]if (check(mid)) r mid;else l mid 1;}return l; }int bsearch_2(int l, int r) {while (l < r){int mid l r …

科普馆VR技术展现安全场景,构建安全教育新标杆!

随着VR技术的快速发展&#xff0c;其所衍生出的互动装置&#xff0c;悄无声息地渗透进了我们生活的每个角落&#xff0c;就连那严谨而重要的安全教育领域&#xff0c;也没能逃出这神奇魔法的“魔爪”&#xff0c;这种VR互动设备简直就是安全知识传递的小能手&#xff0c;那么&a…

SpringCloud系列(7)--Eureka服务端的安装与配置

前言&#xff1a;上一章节我们介绍了Eureka的基础&#xff0c;本章节则介绍Eureka服务端的安装与配置 Eureka架构原理图 1、创建Eureka Server端服务注册中心模块 (1)在父工程下新建模块 (2)选择模块的项目类型为Maven并选择模块要使用的JDK版本 (3)填写子模块的名称&#xf…

llama-factory SFT 系列教程 (四),lora sft 微调后,使用vllm加速推理

文章目录 文章列表&#xff1a;背景简介llama-factory vllm API 部署融合 lora 模型权重 vllm API 部署HuggingFace API 部署推理API 部署总结 vllm 不使用 API 部署&#xff0c;直接推理数据集 tenplatevllm 代码部署 文章列表&#xff1a; llama-factory SFT系列教程 (一)&a…

SpringMVC(三)【REST 风格】

1、REST 风格 1.1、REST 简介 REST&#xff08;Representational State Transfer&#xff09;&#xff0c;表现形式状态转换 在开发中&#xff0c;它其实指的就是访问网络资源的格式 1.1.1、传统风格资源描述形式 http://localhost/user/getById?id1http://localhost/user…

18 统计网站每日的访问次数

1.将竞赛的数据上传HDFS,查看数据的格式 通过浏览器访问hdfs,查看该文档前面的部分数据 每条数据的字段值之间使用逗号隔开的 &#xff0c;最终时间是第五个自动&#xff0c;获取第五个字段值的中的年月日。 2.通过Idea创建项目mr-raceData ,基础的配置 修改pom.xml,添加依赖 …

一文读懂uniapp中的tabBar底部导航

目录 1. 基本知识2. Demo 1. 基本知识 UniApp 中的 tabBar 是用来在应用程序底部显示可切换的选项卡的组件&#xff0c;通常用于实现底部导航栏 允许用户通过点击不同的选项卡来切换应用程序的不同页面或功能模块 其代码如下&#xff1a; "tabBar":{"color&q…

HoloLens2的Unity应用在电脑上发布成安装包,然后通过wifi安装到设备

一、VS工程中的鼠标右键 二、发布——>创建应用程序包 三、选择【旁加载】 四、选择签名方法&#xff1a; 五、选择和配置包 六、创建完毕 七、网络连接设备 八、登录设备 九、安装app

spring高级篇(二)

1、Aware和InitializingBean Aware和InitializingBean都与Bean的生命周期管理相关。 Aware接口: 概念: Aware接口是Spring框架中的一个标记接口&#xff0c;它表示一个类能够感知到&#xff08;aware of&#xff09;Spring容器的存在及其特定的环境。Spring框架提供了多个Awar…

Android自带模拟器如何获得ROOT权限

如果在模拟器中不能切换到root权限&#xff0c;很可能是镜像使用的不对。 一.选择镜像标准&#xff1a; 1.运行在PC端选X86_64镜像&#xff0c;才能流畅运行 2.不带google api的镜像 二.步骤 在虚拟机管理器中新建AVD&#xff0c;并下载符合要求的镜像文件 三.验证
最新文章