数据结构——优先级队列(堆)

概念:

        在操作数据的时候,操作的数据具有优先级,需要返回最高级别的优先级数据或者添加新对象时就需要用到优先级队列。

        jdk1.8中的PrioriytQueue底层实现了堆这种数据结构实际上,堆其实就是在完全二叉树进行调整而来。

堆:

        对于一个关键码的集合K = { K1,K2,K3……Ki},把所有的元素按完全二叉树的顺序储存方式储存在一个一维数组中

堆的性质:

①孩子结点恒大于父结点(小根堆模式) ②堆总是一棵完全二叉树

注:小根堆模式只要满足孩子结点大于父结点,至于孩子结点之间谁大谁小都无所谓

结构:(以小根堆为例)堆的存储结构,就是按照二叉树的层序遍历去存放

 为什么只堆只能是完全二叉树?非完全二叉树不行吗?

这是因为顺序存储的情况下,由于数组里要存放结点,非完全二叉树会浪费存储空间,导致空间利用率不高

 如何判断有无子树?

对于结点的顺序存储进行编号,如果i=0,那么i对应的结点就是树的根结点;

如果2*i + 1小于结点数,那么结点i的左孩子是2*1+1,否则没有左孩子; 

如果2*i + 2小于结点数, 那么结点i的右孩子是2*i+2,  否则没有右孩子。

 堆的创建:

向下调整:

思路:我们已知如何判断一个节点如何判断有无孩子结点,那么我们只需要遍历数组的时候将对应孩子结点的较大者和双亲结点进行比较大小即可。在每次比较之后继续往下遍历孩子结点,直到整棵树都是小根堆(或者大根堆)模式。

【注】:在左右孩子结点进行比较之前得先判断是否有右孩子结点,避免出现下标越界的错误!

以此树为例

大根堆模式下这棵树应该长这样子

public class Heap {public int[] elem;public int usedsize;public Heap(int n){elem = new int[n];}//插入元素public void initElem(int[] array){for (int i = 0; i < array.length; i++) {elem[i] = array[i];usedsize++;}}//创建大根堆public void createBigHeap(){for (int parent = (usedsize-1-1)/2; parent >=0 ; parent--) {sifedown(parent, usedsize);}}//向下调整private void sifedown(int parent, int end){int child = 2*parent+1;while(child<end){if(elem[child]< elem[child+1] && child+1 < usedsize)     //确保右子树一定存在再进行比较,child一定是左右孩子结点的较大者{child++;}if(elem[child] > elem[parent]){Swap(child,parent);parent = child;                  //交换完之后再向下调整child = 2*parent+1;}else {break;}}}//大根堆交换public void Swap(int i,int j){int tmp;tmp = elem[i];elem[i] = elem[j];elem[j] = tmp;}public static void main(String[] args) {int[] array = {77,13,46,20,28,37,55,19,9};    //给定一个数组Heap heap = new Heap(9);heap.initElem(array);heap.createBigHeap();                         //创建大根堆模式System.out.println("=============================");}
}

通过调试来检验是否成功向下调整

  再以小根堆模式为例,来深刻体会向下调整的整个过程~

我们的整体思路是:先调整一棵子树,保证子树一定是小根堆,然后再从子树的父节点向下做调整。

    private void Siftdown(int parent, int end){int child = 2*parent+1;while(child<end){if(child+1 < usedsize && elem[child]>elem[child+1]  )     //确保右子树一定存在再进行比较{child++;}if(elem[child] < elem[parent]){Swap(child,parent);parent = child;                  //交换完之后再向下调整child = 2*parent+1;}else {break;}}}


堆的插入和删除:

 插入:

        先判断是否需要扩容,然后再直接将元素插入到usedsize的位置,每次插入一个元素都需要做一次向上调整。(扩容与顺序表扩容的步骤相同)

  //插入public void offer(int val){//判断是否需要扩容if(isfull()){this.elem = Arrays.copyOf(elem,2*elem.length);}//插入元素elem[usedsize] = val;usedsize++;siftup(usedsize-1);}

删除:

        将堆的根节点和最底层最末尾位置的结点交换,将usedsize减一再做一次向下调整

  //删除public int poll(){int tmp = elem[0];Swap(0,usedsize-1);usedsize--;siftdown(0,usedsize);return tmp;}

在了解了堆的特性以及底层结构是如何实现的之后,接着来了解底层接口PriorityQueue

常用接口PriorityQueue

在java的集合框架里,提供的优先级队列有两种:PriorityqQueue和PriorityBlockingQueue,前者是线程不安全的,后者是线程安全的

注意:

1、用PriorityQueue接口的时候一定要先导包

import java.util.PriorityQueue;

 2、PriorityQueue中放置的元素必须能够比较大小不能插入无法比较大小的对象,不然就会抛出异常ClassCastException

3、插入的对象不能是null,不然会抛出空指针异常

4、没有容量限制,可以随意插入多个元素,内部可以自动扩容

5、插入和删除元素的时间复杂度是  log2 n  (以2为底的对数)

6、PriorityQueue底层用了堆数据结构

7、PriorityQueue默认是小堆模式——即每次获取到的元素都是最小的元素

常用接口介绍:

 1.优先队列的构造

在PriorityQueue接口中就能观察到实例化一个优先级队列最常用到的三个构造方法

先看看接口中都有哪些变量

然后就是相关的构造方法

关于比较器,我们可以回顾一下java中对象的三种比较方式:

①覆写基类的equals。因为用户实现自定义类型都继承自Object类,而Object类中提供了equals方法,我们想要按照对象中的内容来调整,那就得重写基类的equals方法基本类型的比较大小可以直接用 == ,>, <进行比较,引用类型最好还是调用equals。

public class people
{public static void main(String[] args){public int age;public String name;public people(int age, String name){this.age = age;this.name = name;}@Overridepublic boolean equals(Object o){//和自己比较if(this == o){return true;}//如果o不是people的子类或者O是null对象if(o == null ||!(o instanceof people)){return false;}//基本类型可以直接比较,但是引用类型最好还是调用其equals方法people p1 = (people)o;return age == p1.age && name.equals(p1.name);}}
}

② 基于Comparable接口类的比较

Comparable是JDK提供的泛类比较接口类,源码如下(绿色的是注释,可以不理):

对用户自定义类型,想要按照大小与方式进行比较时,在自定义类的时候实现Comparable接口即可,然后重写Comparable 中的方法。

例如:

我们自定义一个类people,按照年龄age大小进行比较,那么就是重写Comparable中的方法compareTo

class people implements Comparable<people> {public int age;public String name;public people(int age ,String name) {this.age = age;this.name = name;}@Overridepublic int compareTo(people o) {return this.age-o.age;}
}

实例化两个对象,调用compareTo进行比较,观察返回的结果

我们可以观察到返回结果是一个大于0的数字,这就证明按照年龄大小的比较方式,p1>p2

那么,现在又有一个问题:如果我想要按照名字来进行比较,这要怎么修改?

这就要用到第三种比较方式,比较器了! 

③基于比较器比较 

用户自定义比较类,实现Comparator接口 (绿色注释可不理)

返回值>0表示o1指向的对象大于o2指向的对象,==0 就是相当,<0就是o1指向的对象小于o2指向的对象。

此时我们就可以解决刚刚的问题了,自定义一个类作为我们想要的比较器,在类中覆写Comparator中的compare方法

class NameComparator implements Comparator<people>{@Overridepublic int compare(people o1, people o2) {return o1.name.compareTo(o2.name);}
}

在了解了比较器之后,我们来进一步了解为什么PriorityQueue是默认建的小根堆

    public static void main(String[] args) {people p1 = new people(10,"zhangsan");people p2 = new people (8,"lisi");PriorityQueue<people> priorityQueue = new PriorityQueue<>();priorityQueue.offer(p1);priorityQueue.offer(p2);}

 我们看一下PriorityQueue接口中,offer的原码

再看siftup我们可以观察到:如果你在实例化PriorityQueue的时候没有传一个比较器作为参数,那么就默认是用接口中的比较器,调用接口中的则默认为小根堆模式

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

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

相关文章

C++STL--------vector

文章目录 一、vector常用接口介绍1、initializer_list2、接口有很多类似3、typeid(类型).name()4、find() 函数5、内置类型构造 二、vector()常用接口模拟实现 截图来源网站&#xff1a;https://legacy.cplusplus.com/reference/vector/vector/ 一、vector常用接口介绍 是一个…

哪种护眼大路灯孩子用着最好?公认最好的护眼大路灯

哪种护眼大路灯孩子用着最好&#xff1f;最近也有不少家长关注到了孩子视力健康的这个情况&#xff0c;很着急开始寻找各种能够减少孩子因为不良光线影响视力健康的方法&#xff0c;其中大路灯以良好的表现成为家长们的首选&#xff0c;但快速发展的市场中&#xff0c;却涌入了…

【C】C语言常见概念~

C语言常见概念 转义字符 转义字符&#xff0c;顾名思义&#xff0c;转变原来意思的字符 比如 #include <stdio.h> int main() {printf("abcndef");return 0; }输出的结果为&#xff1a; 将代码修改一下&#xff1a; #include <stdio.h> int main(…

双目视觉搭配YOLO实现3D测量

一、简介 双目&#xff08;Stereo Vision&#xff09;技术是一种利用两个相机来模拟人眼视觉的技术。通过对两个相机获取到的图像进行分析和匹配&#xff0c;可以计算出物体的深度信息。双目技术可以实现物体的三维重建、距离测量、运动分析等应用。 双目技术的原理是通过两…

SpringBoot基础(五):集成JUnit5

SpringBoot基础系列文章 SpringBoot基础(一)&#xff1a;快速入门 SpringBoot基础(二)&#xff1a;配置文件详解 SpringBoot基础(三)&#xff1a;Logback日志 SpringBoot基础(四)&#xff1a;bean的多种加载方式 SpringBoot基础(五)&#xff1a;集成JUnit5 目录 一、JUnit…

AIGC毕设项目分享:基于RAG的数字人对话系统及其应用

本研究的主要目标是设计并实现一个基于检索增强生成&#xff08;RAG&#xff09;技术的数字人对话系统&#xff0c;旨在提升数字人系统在多轮对话中的上下文管理、情境感知能力以及动态内容生成效果。系统结合了深度学习中的最新大语言模型技术&#xff0c;通过引入RAG框架来增…

K8S配置MySQL主从自动水平扩展

前提环境 操作系统Ubuntu 22.04 K8S 1.28.2集群&#xff08;1个master2个node&#xff09; MySQL 5.7.44部署在K8S的主从集群 metrics-server v0.6.4 概念简介 在K8s中扩缩容分为两种 ●Node层面&#xff1a;对K8s物理节点扩容和缩容&#xff0c;根据业务规模实现物理节点自动扩…

爬虫案例——网易新闻数据的爬取

案例需求&#xff1a; 1.爬取该新闻网站——&#xff08;网易新闻&#xff09;的数据&#xff0c;包括标题和链接 2.爬取所有数据&#xff08;翻页参数&#xff09; 3.利用jsonpath解析数据 分析&#xff1a; 该网站属于异步加载网站——直接网页中拿不到&#xff0c;需要…

MySQL-08.DDL-表结构操作-创建-案例

一.MySQL创建表的方式 1.首先根据需求文档定义出原型字段&#xff0c;即从需求文档中可以直接设计出来的字段 2.再在原型字段的基础上加上一些基础字段&#xff0c;构成整个表结构的设计 我们采用基于图形化界面的方式来创建表结构 二.案例 原型字段 各字段设计如下&…

深入理解线性表--顺序表

目录 顺序表- Seqlist -> sequence 顺序 list 表 顺序表的概念 问题与解答 顺序表的分类 静态顺序表 动态顺序表 问题与解答(递进式) 动态顺序表的实现 尾插 头插 尾删 头删 指定位置插入 指定位置删除 销毁 总结 前言&#xff1a;线性表是具有相同特性的一类数据结构…

2024 年 04 月编程语言排行榜,PHP 排名创新低?

编程语言的流行度总是变化莫测&#xff0c;每个月的排行榜都揭示着新的趋势。2024年4月的编程语言排行榜揭示了一个引人关注的现象&#xff1a;PHP的排名再次下滑&#xff0c;创下了历史新低。这种变化对于PHP开发者和整个技术社区来说&#xff0c;意味着什么呢&#xff1f; P…

现代数字信号处理I-P3 MVUE学习笔记

目录 1. 参数估计问题的提出与本质 2. 估计的性质 2.1 Ancillary&#xff08;多余估计&#xff09; 例1&#xff0c;Ancillary估计量 2. Uniformly Optimal 3. Sufficiency充分性 3.1 统计量充分性定义 例2&#xff1a;利用充分统计量定义获取伯努利分布的充分统计量 …

Anaroute - 理论学习(一)

一、贡献&#xff1a; 框架能够在考虑特定约束的同时&#xff0c;高效地完成复杂AMS设计的布线&#xff0c;并实现签署质量的性能。 提出了一种对称性约束的分配算法&#xff0c;根据引脚位置分配合适的网络匹配要求新的引脚聚类策略&#xff0c;以实现规律性的布线模式&…

微知-Bluefield DPU使用flint烧录固件报错MFE_NO_FLASH_DETECTED是什么?MFE是什么?

文章目录 背景一些报错场景MFE是什么&#xff1f;有哪些MFE 背景 在DPU的fw操作flint的时候&#xff0c;很多命令都会报这个错误&#xff1a;MFE_NO_FLASH_DETECTED&#xff0c;早期很疑惑并且猜测MFE是Mellanox Firmware Engine。实际并不是&#xff0c;具体还得走到mellanox…

2014年国赛高教杯数学建模B题创意平板折叠桌解题全过程文档及程序

2014年国赛高教杯数学建模 B题 创意平板折叠桌 某公司生产一种可折叠的桌子&#xff0c;桌面呈圆形&#xff0c;桌腿随着铰链的活动可以平摊成一张平板&#xff08;如图1-2所示&#xff09;。桌腿由若干根木条组成&#xff0c;分成两组&#xff0c;每组各用一根钢筋将木条连接…

2024 第一次周赛

A: 题目大意 骑士每连续 i 天每天会得到 i 个金币&#xff0c;&#xff08;i 1&#xff0c; 2&#xff0c; 3 &#xff0c; …&#xff09;,那么展开看每一天可以得到的金币数&#xff1a;1 2 2 3 3 3 4 4 4 5 5 5 5 5 … 可以发现就是1个1 &#xff0c;2个2, 3个3…,那么我…

php 生成随机数

记录:随机数抽奖 要求:每次生成3个 1 - 10 之间可重复(或不可重复)的随机数,10次为一轮,每轮要求数字5出现6次、数字4出现3次、…。 提炼需求: 1,可设置最小数、最大数、每次抽奖生成随机数的个数、是否允许重复 2,可设置每轮指定数字的出现次数 3,可设置每轮的抽奖…

一维数组的引用

#define SIZE 5 int main(void) { int i 0; int arr[SIZE] { 86,85,85,896,45 };//同理五个数据只是偶然&#xff0c;可能会更多 //输入 for (i 0;i < SIZE;i) { printf("请输入你的第%d个值&#xff1a;",i1); scanf_s(&…

Spark常用RDD算子:transformation转换算子以及action触发算子

文章目录 1. 算子&#xff08;方法&#xff09;介绍2. 常用transformation算子2.1 map 2.2 flatMap2.3 filter2.4 distinct2.6 groupBy2.7 sortBy()2.8 k-v数据[(k,v),(k1,v1)] 3. 常用action算子 1. 算子&#xff08;方法&#xff09;介绍 rdd中封装了各种算子方便进行计算&a…

【Linux网络编程】网络基础 | Socket 编程基础

&#x1f308;个人主页&#xff1a; 南桥几晴秋 &#x1f308;C专栏&#xff1a; 南桥谈C &#x1f308;C语言专栏&#xff1a; C语言学习系列 &#x1f308;Linux学习专栏&#xff1a; 南桥谈Linux &#x1f308;数据结构学习专栏&#xff1a; 数据结构杂谈 &#x1f308;数据…