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

概念:

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

        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;却涌入了…

Golang 逃逸分析(Escape Analysis)理解与实践篇

Golang 逃逸分析&#xff08;Escape Analysis&#xff09;理解与实践篇 文章目录 1.逃逸分析2.相关知识&#xff08;栈、堆、GC分析&#xff09;3.逃逸分析综合-实践 demo 逃逸分析&#xff08;Escape Analysis&#xff09;是编译器在编译期进行的一项优化技术&#xff0c;是Gl…

Qt QTableWidget多行表头、表头折行显示

表头折行显示 //方法一QVector<QString> chNames;chNames<<"表头1"<<"表头2长命名abcdefg";ui.tableWidget->setColumnCount(chNames.size()1);ui.tableWidget->setHorizontalHeaderItem(0, new QTableWidgetItem(QString::fromL…

【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…

敏捷开发 与 Scrum

‌敏捷开发的概念起源于20世纪90年代末期&#xff0c;旨在解决传统软件开发方法中存在的效率低下问题。‌ 传统软件开发方法因其繁琐的过程和对文档的严格要求&#xff0c;导致了“重型化危机”&#xff0c;这使得开发效率大幅下降。为了应对这些问题&#xff0c;敏捷方法应运…

微信小程序 - 01 - 一些补充和注意点(补充ing...)

目录 一、节流二、在一个发请求的函数中&#xff0c;只有发生下拉动作&#xff0c;才执行关闭下拉代码 最近在学微信小程序&#xff0c;把学习过程中的一些补充和注意点总结一下&#xff0c;内容会比较简单&#xff0c;因为只涉及基础知识&#xff0c;供个人参考 一、节流 情…

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;根据业务规模实现物理节点自动扩…

Python列表专题:list与in

Python是一种强大的编程语言,其中列表(list)是最常用的数据结构之一。列表允许我们存储多个元素,并且可以方便地进行各种操作。在Python中,in运算符被广泛用于检测元素是否存在于列表中。本文将深入探讨Python列表及其与in运算符的结合使用。 1. Python列表的基础 1.1 什…

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

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

项目开发--基于docker实现模型容器化服务

背景 1、docker-compose build 和 docker-compose up -d分别是什么作用&#xff1f; 2、如何进入新构建的容器当中 3、模型保存的方法区别 4、如何让docker容器启动的时候能使用cuda进行模型推理加速 5、如何实现容器的迭代 解决方案 问题1 docker-compose build 和 docker…

WindowsAPI|每天了解几个winAPI接口之Iphlpapi.h网络配置相关文档详细分析三

上一篇&#xff1a;WindowsAPI|每天了解几个winAPI接口之Iphlpapi.h网络配置相关文档详细分析二 如果有错误欢迎指正批评&#xff0c;在此只作为科普和参考。 文章目录 GetTcpTable&GetExtendedTcpTable&#xff1a;获取TCP连接的信息GetOwnerModuleFromTcpEntry&#xff1…

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;利用充分统计量定义获取伯努利分布的充分统计量 …

服务器源IP暴露后的安全风险及防御措施

在互联网安全领域&#xff0c;服务器的源IP地址泄露可能成为黑客攻击的切入点。本文将列举十种常见的攻击类型&#xff0c;并提供相应的防御建议&#xff0c;帮助管理员们更好地保护服务器免受潜在威胁。 一、引言 服务器源IP地址的暴露意味着攻击者可以直接针对服务器发起攻击…