【堆的认识及其优先级队列】java代码实现,保姆级教程学习堆和优先级队列

news2024/11/13 9:48:33

前言:
大家好,我是良辰丫💞💞⛽,我们又见面了,前面我们讲了用链表实现的二叉树,今天我们来接触的概念,堆是一种特殊的二叉树,只不过咱们的对底层原理是数组,堆也是我们在做题中经常见到的,那么,接下来我们就慢慢的去接触堆,认识堆,理解堆,掌握堆。有疑惑可以和我一起讨论交流哦,期待大家的来访。🍬🍬🍬

🧑个人主页:良辰针不戳
📖所属专栏:java数据结构
🍎励志语句:生活也许会让我们遍体鳞伤,但最终这些伤口会成为我们一辈子的财富。
💦期待大家三连,关注,点赞,收藏。
💌作者能力有限,可能也会出错,欢迎大家指正。
💞愿与君为伴,共探Java汪洋大海。

在这里插入图片描述


目录

  • 1、堆
    • 1.1 堆的概念
    • 1.2 堆的基本性质
    • 1.3 堆的创建
    • 1.4 堆插入数据
    • 1.5 堆删除数据
  • 2、优先级队列
    • 2.1 初识优先级队列
    • 2.2 比较器Comparator实现优先队列大根堆


1、堆

1.1 堆的概念

在逻辑上是一种完全二叉树,以顺序结构存储的方式存储的数据,堆的模拟一般用数组,其底层原理往往也是数组。堆分为大根堆和小根堆。

  • 大根堆(最大堆):父亲节点比所有孩子节点都大。
  • 小根堆(最小堆):父亲节点比所有孩子节点都小。

如下图就是一个板板正正的大根堆,每个父亲节点都比所有孩子节点都大,如果去掉下标为6的节点,还可称为大根堆,但是去掉了4号下标的节点就不是堆了,因为堆的概念是完全二叉树,那么为啥这么定义呢?因为在建堆的过程中需要一个个调整,如果不是完全二叉树,就不会调整成唯一的一个序列。

在这里插入图片描述

总结:

  • 堆的父亲节点总是大于或者小于所有孩子节点。
  • 堆总是一棵完全二叉树。

1.2 堆的基本性质

通过上面我们了解到是一种顺序存储的完全二叉树,对于非完全二叉树,不适合使用顺序方式进行存储,因为为了能够还原二叉树,空间中必须要存储空节点,就会导致空间利用率比较低。

下图为一个小根堆图,研究性质可以参考下图。

在这里插入图片描述

i表示节点的下标

  • 如果i为0,则i表示的节点为根节点,否则i节点的双亲节点为 (i - 1)/2
  • 如果2 * i + 1 小于节点个数,则节点i的左孩子下标为2 * i + 1,否则没有左孩子
  • 如果2 * i + 2 小于节点个数,则节点i的右孩子下标为2 * i + 2,否则没有右孩子

1.3 堆的创建

堆的创建,顾名思义就是把一个个的数据插入到数组中,然后调整即可,一个个传数据太麻烦了,我们直接传入一个数组。调整的时候我们采用向下调整。

在这里插入图片描述

  1. 让parent标记需要调整的节点,child标记parent的左孩子(注意:parent如果有孩子一定先是有左孩子)
  2. 如果parent的左孩子存在,即:child < size, 进行以下操作,直到parent的左孩子不存在
    parent右孩子是否存在,存在找到左右孩子中最小的孩子,让child进行标
    将parent与较小的孩子child比较,如果:
    parent小于较小的孩子child,调整结束
    否则:交换parent与较小的孩子child,交换完成之后,parent中大的元素向下移动,可能导致子
    树不满足对的性质,因此需要继续向下调整,即parent = child;child = parent*2+1; 然后继续2。

在这里插入图片描述

下面以创建大根堆为例子。

public void createHeap() {
//usedSize记录堆中元素个数
        for (int parent = (usedSize-1-1)/2; parent >= 0 ; parent--) {
            shiftDown(parent,usedSize);
        }
    }

   //向下调整
    private void shiftDown(int parent,int len) {
    //知道父亲节点,然后扎到左孩子
        int child = 2*parent + 1;
        //要有左孩子
        while (child < len) {
            //一定是有右孩子的情况下
            if(child+1 < len && elem[child] < elem[child+1]) {
                child++;
            }
            //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;
            }
        }
    }
  • 在调整以parent为根的二叉树时,必须要满足parent的左子树和右子树已经是堆了才可以向下调整。
  • 调整情况下,最坏的时候,从根一路比较到叶子,比较的次数为完全二叉树的高度。

那么,问题又来了,一颗普通的完全二叉树怎么调整成堆呢?
找倒数第一个非叶子节点,从该节点位置开始往前一直到根节点,遇到一个节点,应用向下调整

public static void createHeap(int[] array) {
int root = ((array.length-2)>>1);
for (; root >= 0; root--) {
shiftDown(array, root);
}
}

注意:
建堆的时间复杂度为O(N)。

1.4 堆插入数据

  • 先将待插入元素放到下标为usedSize的位置。(空间不够时需要扩容)
  • 进行向上调整,直到成为堆。

有的伙伴可能会疑惑了,刚刚学习了向下调整,下载插入数据操作为什么又要向上调整呢?

在这里插入图片描述

private 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;
            }
        }
    }
    //向上调整建堆的时间复杂度:N*logN
    public void offer(int val) {
        if(isFull()) {
            //扩容
            elem = Arrays.copyOf(elem,2*elem.length);
        }
        elem[usedSize++] = val;
        //向上调整
        shiftUp(usedSize-1);
    }

1.5 堆删除数据

  • 将堆顶元素对堆中最后一个元素交换
  • 将堆中有效数据个数减少一个
  • 对堆顶元素进行向下调整

在这里插入图片描述

 public void pop() {
        if(isEmpty()) {
            return;
        }
        swap(elem,0,usedSize-1);
        usedSize--;
        shiftDown(0,usedSize);
    }

    public boolean isEmpty() {
        return usedSize == 0;
    }
    private void swap(int[] array,int i,int j) {
        int tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }

2、优先级队列

说起对列,大家肯定不陌生,因为咱们前面的文章中学习过对列,对列是一种先进先出的数据结构,那么这里的优先级队列又是什么呢?下面我们简单通过一个代码对优先级队列做一个认识。

2.1 初识优先级队列

public static void main(String[] args) {
        PriorityQueue<Integer> priorityQueue = new PriorityQueue();
        priorityQueue.offer(3);
        priorityQueue.offer(4);
        priorityQueue.offer(1);
        priorityQueue.offer(2);
        System.out.println(priorityQueue.poll());
        System.out.println(priorityQueue.poll());
        System.out.println(priorityQueue.poll());
        System.out.println(priorityQueue.poll());
    }

在这里插入图片描述

咿呀,奇迹出现了,通过上面的运行截图,大家可以发现,优先级队列并不遵守先进先出,而是最小的数据先出来,由此呢?我们可以把它当成一种小根堆

看到这里,大家又产生疑惑,默认为小根堆,那么如果我们想要使用优先队列作为大根堆呢?

办法当然是有的呀,这里我们引入了比较器。那么究竟如何实现呢?我们慢慢往下看。

2.2 比较器Comparator实现优先队列大根堆

看到这里,可能大家会感到疑惑,什么是比较器呢?
java中的对象提供的方法只能比较对象是否相等,而不能比较大小,java提供了一些接口,可以指定对象的属性进行比较,比如,根据学生信息的年龄进行排序。
呜呜呜,看了上面的解释还不明白,没关系,在我上一篇文章中详细介绍了比较器,如果大家对比较器有疑惑,请去具体学习比较器。
对象的比较,点击跳转文章

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

class IntCmp implements Comparator<Integer> {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2-o1;
    }
}
public class Test02 {
    public static void main(String[] args) {
        PriorityQueue priorityQueue = new PriorityQueue<>(new IntCmp());
        priorityQueue.offer(3);
        priorityQueue.offer(4);
        priorityQueue.offer(1);
        priorityQueue.offer(2);
        System.out.println(priorityQueue.poll());
        System.out.println(priorityQueue.poll());
        System.out.println(priorityQueue.poll());
        System.out.println(priorityQueue.poll());
    }
}

在这里插入图片描述

奇迹出现了优先队列通过比较器Comparator修改成了大根堆

注意:
上面的IntCmp类中有自动装包拆包的过程,这里不做详细说明,简单提及一下,Comparator的类型参数不能为基本数据类型,所以只能用包装类Integer,如果想了解更多装包拆包大家可以去自主学习,或者我以后的文章也会更新的。

后序:
堆与优先级队列的学习愉快的结束了,大家肯定受益颇深🐥🐥🐥,知识点全都不难,所谓难的东西也是一个个小小的知识点汇集而成的,不要着急,学习是一个不断探索的过程,心若向阳,路便花开,继续加油🍗🍗🍗!!!

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

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

相关文章

大数据监控平台-Prometheus监控Hadoop

简介 本篇主要是使用jmx配合Prometheus监控大数据平台 前提 链接&#xff1a;https://pan.baidu.com/s/1c6nsjOKw4-a_Wqr82l0QhQ 提取码&#xff1a;yyds --来自百度网盘超级会员V5的分享 先安装好Prometheus Flink(Pometheus监控)_顶尖高手养成计划的博客-CSDN博客_${en…

金融实践 | 信创存储 打造安全可控的金融数据底座

本文刊登于《金融电子化》杂志 2023 年 1 月上&#xff0c;作者为中国出口信用保险公司信息科技部张倩&#xff0c;曲文非&#xff0c;庞松松&#xff0c;康达。 2022 年初&#xff0c;中国人民银行《金融科技发展规划&#xff08;2022—2025 年&#xff09;》和银保监会《关于…

JAVA语言实验 实验 ( 二 )

JAVA语言实验 :实验 &#xff08; 一 &#xff09; JAVA语言实验 :实验 &#xff08; 二 &#xff09; JAVA语言实验 :实验 &#xff08; 三 &#xff09; 一、实验目的 &#xff08;1&#xff09;熟悉 Java 图形界面的基本设计。 &#xff08;2&#xff09;熟悉 Java 界面的菜…

【LeetCode每日一题】【2023/1/31】2319. 判断矩阵是否是一个 X 矩阵

文章目录2319. 判断矩阵是否是一个 X 矩阵方法1&#xff1a;直接遍历2319. 判断矩阵是否是一个 X 矩阵 LeetCode: 2319. 判断矩阵是否是一个 X 矩阵 简单\color{#00AF9B}{简单}简单 如果一个正方形矩阵满足下述 全部 条件&#xff0c;则称之为一个 X 矩阵 &#xff1a; 矩阵对…

spring boot文档阅读笔记——02

spring boot文档阅读笔记——01 目录标题一、日志&#xff08;一&#xff09;slf4j logback&#xff08;spring boot默认方式&#xff09;1. 获取日志对象方式&#xff1a;2. 设置日志级别&#xff1a;3. 设置日志格式&#xff1a;4. 输出到日志文件&#xff1a;5. 日志文件设置…

关于python的mediapipe库踩过的坑

大家好&#xff0c;我是csdn的博主&#xff1a;lqj_本人 这是我的个人博客主页&#xff1a;lqj_本人的博客_CSDN博客-微信小程序,前端,vue领域博主lqj_本人擅长微信小程序,前端,vue,等方面的知识https://blog.csdn.net/lbcyllqj?spm1000.2115.3001.5343 哔哩哔哩欢迎关注&…

Nginx 常用配置汇总!

众所周知&#xff0c;Nginx 是 Apache服务不错的替代品。其特点是占有内存少&#xff0c;并发能力强&#xff0c;事实上 Nginx 的并发能力在同类型的网页服务器中表现较好&#xff0c;因此国内知名大厂例如&#xff1a;淘宝&#xff0c;京东&#xff0c;百度&#xff0c;新浪&a…

1.10 golang 切片Slice

1. 切片Slice 需要说明&#xff0c;slice 并不是数组或数组指针。它通过内部指针和相关属性引用数组片段&#xff0c;以实现变长方案。 1. 切片&#xff1a;切片是数组的一个引用&#xff0c;因此切片是引用类型。但自身是结构体&#xff0c;值拷贝传递。2. 切片的长度可以改变…

零基础机器学习做游戏辅助第六课--猫狗数据集认识卷积神经网络(二)

一、初识卷积 上一课我们已经将图像数据进行了预处理,这节课的重点就是学习卷积神经网络,到底什么是卷积,我们看图 input是我们输入的图像,Kernel是我们设置的3x3卷积核,卷积层将图像和卷积核进行计算提取特征输出神经元。

代码随想录算法训练营第35天 回溯算法 java :455.分发饼干 376. 摆动序列53. 最大子序和

文章目录贪心算法思路LeetCode 455.分发饼干题目详解LeetCode 376. 摆动序列题目详解思路示图LeetCode 53. 最大子序和题目详解思路示图总结贪心算法思路 以局部最优带动全局最优 LeetCode 455.分发饼干 题目详解 我做的是采用 优先满足胃口的思路。 对每个孩子 i&#xff…

【c#系列】PDF进行操作-浏览、分割、合并、插入、删除(2)

这节我们主要实现缩小、旋转、打印、分割、合并、放大等功能 1、 放大功能 单击放大按钮&#xff0c;实现PDF放大预览&#xff0c;效果如下&#xff1a; 设计代码&#xff1a; System.Windows.Forms.ToolStripButton FangDaBT_Tool;FangDaBT_Tool new System.Windows.Form…

GBase GCDW云数仓阿里云版免费试用来了!

GBase GCDW云原生数据仓库&#xff08;GCDW&#xff09;在阿里云计算巢上提供免费试用了&#xff01;简单 3 步&#xff0c;即可获得一个免费试用的GCDW服务实例&#xff0c;您可以定制该服务实例的云主机规格和数据库计算服务节点数等实例参数&#xff0c;该免费试用支持的数据…

LabVIEW NI CompactRIO控制器:性能和吞吐量基准测试

LabVIEW NI CompactRIO控制器&#xff1a;性能和吞吐量基准测试CompactRIO控制器基于LabVIEW RIO架构&#xff0c;采用了功能强大的64位Intel Atom E3800片上系统(SoC)和Xilinx Kintex7 FPGA等最新技术。Intel Atom SoC提供了极高的性能和丰富的功能&#xff0c;包括集成式GPU和…

数据结构实验二 :二叉树的操作与实现

数据结构实验一:线性表,堆栈和队列实现 数据结构实验二 :二叉树的操作与实现 数据结构实验三: 图的操作与实现 数据结构实验四 : 查找和排序算法实现 文章目录一、实验目的&#xff1a;二、使用仪器、器材三、实验内容及原理1、教材P247实验题1&#xff1a;实现二叉树的各种基本…

Mybatis 笔记

一、mybatis简介 1.1 框架概念 软件的半成品&#xff0c;完成软件开发过程中的通用操作&#xff0c;实现特定的功能&#xff0c;从而简化开发人员在软件开发中的步骤&#xff0c;提升开发效率。 1.2 常用框架 MVC框架&#xff1a;简化servlet的开发步骤&#xff0c;与前端交…

java实现oracle和mysql的group by分组功能|同时具备max()/min()/sum()/case when 函数等功能

一、前言oracle和mysql的group by 分组功能大家应该清楚&#xff0c;那如何使用java实现同样的功能呢比如下面这个表idnameagemathEnglish10yujianlin2092.5103ww84102520102611036310351020我们需要按id分组&#xff0c;求最大age和math累计成绩我们的sql应该这样写select id,…

Linux下的安装环境

目录 软件安装常识 Linux软件安装生态 Linux软件生态的本土化 yum的三板斧&#xff1a;查找、安装、卸载 yum补充的3个小知识 软件安装常识 我们知道Linux下有一条命令可以下载安装指令&#xff0c;那就是yum。在了解yum之前得先说一下Linux的整体安装环境。 Linux下用y…

每日学术速递2.1

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.Cv 1.SeaFormer: Squeeze-enhanced Axial Transformer for Mobile Semantic Segmentation 标题&#xff1a;SeaFormer:用于移动语义分割的挤压增强型轴向变换器 作者&#xff1a; Qian…

从 await-to-js 到 try-run-js

之前在做 code review 时候发现有同事使用 try catch 包装了一堆异步代码&#xff0c;于是个人就觉得很奇怪&#xff0c;难道不应该只 catch 可能出问题的代码吗&#xff1f;同事告诉我说 try catch 太细的话会出现内外作用域不一致&#xff0c;需要提前声明变量。 let res: D…

【微服务】微服务保护Sentinel

微服务保护Sentinel1.初识Sentinel1.1.雪崩问题及解决方案1.1.1.雪崩问题1.1.2.超时处理1.1.3.仓壁模式1.1.4.断路器1.1.5.限流1.1.6.总结1.2.服务保护技术对比1.3.Sentinel介绍和安装1.3.1.初识Sentinel1.3.2.安装Sentinel1.4.微服务整合Sentinel2.流量控制2.1.簇点链路2.1.快…