当前位置: 首页 > news >正文

【数据结构】关于优先级队列(堆),你了解内部原理吗?(超详解!!!)

前言:

🌟🌟Hello家人们,这期讲解二叉树的遍历,希望你能帮到屏幕前的你。

🌈上期博客在这里:http://t.csdnimg.cn/EdeWV

🌈感兴趣的小伙伴看一看小编主页:GGBondlctrl-CSDN博客

目录

📚️1.优先级队列 

1.1优先级队列的概念

📚️2.优先级队列的模拟(重点)

2.1什么是堆

2.2堆的存储

2.3堆的创建🌟🌟

1.引言

2.思路分析

3.代码实现

2.4堆的插入 🌟🌟

1.思路 

2.代码实现

2.5堆的删除🌟🌟

1.思路

2.代码实现

📚️3.常用接口的介绍

3.1PriorityQueue的特性

3.2PriorityQueue常用接口介绍 

3.3其他函数功能介绍


📚️1.优先级队列 

1.1优先级队列的概念

        前面介绍过队列,队列是一种先进先出(FIFO)的数据结构,但有些情况下,操作的数据可能带有优先级,一般出队列时,可能需要优先级高的元素先出队列,该中场景下,使用队列显然不合适。

比如:在手机上玩游戏的时候,如果有来电,那么系统应该优先处理打进来的电话;初中那会班主任排座位时可能会让成绩好的同学先挑座位

       在这种情况下,数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象。这种数据结构就是优先级队列(Priority Queue)。

📚️2.优先级队列的模拟(重点)

2.1什么是堆

       如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

       总结:总的来说,根结点分为左右两棵树,每棵树都满足根结点大于(小于)其子结点,叫做大根堆(小根堆)

 

2.2堆的存储

从堆的概念可知,堆是一棵完全二叉树,因此可以层序的规则采用顺序的方式来高效存储,

对于完全二叉树有以下性质:

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

• 如果2 * i + 2 • 小于节点个数,则节点i的右孩子下标为2 * i + 2,否则没有右孩子

这里小编在前几期二叉树讲过博客地址: http://t.csdnimg.cn/bnfaS

2.3堆的创建🌟🌟

1.引言

       对于堆而言,虽然是一个完全二叉树,但是在创建过程中其实并没有用到二叉树的相关知识,其内部原理是顺序表,数组的运用,并且由于要涉及根结点与子结点的大小比较所以要用到上述公式,进行解决。

2.思路分析

图解:

       在每次判断后我们要进行子结点的大小比较,然后进行与根结点的大小比较,然后再进行交换(这里小编将创建大根堆),然后判断交换后的根结点是否满足大根堆条件,此时就要向下再次判断,最终实现大根堆的创建

3.代码实现

1.对的初始化实现创建

public class PriorityQueue {public int usedSize=9;public int elme[];public PriorityQueue(){this.elme=new int[usedSize];}public void creatArray(int array[]){for (int i = 0; i <array.length ; i++) {elme[i]= array[i];}}

注解:小编这里为了方便在主函数实现数据替换。

2.实现堆的创建

 public int[] creatQueue(){creatArray(elme);for (int i = (elme.length-2)/2; i >=0 ; i--) { //对每个根节点进行调整downQueue(elme,i);}return elme;}//此时进行向下调整public void downQueue(int elem[],int parent){ int child=2*parent+1;                          //每个根结点的孩子结点while (child<usedSize){                        //循环实现交换if(elem[child]<elem[child+1]&&(child+1)< usedSize){child++;}if(elem[parent]<elem[child]){              //孩子结点的交换swap(parent,child,elem);}parent=child;                       //判断交换后的树的每个根结点与子结点child=parent*2+1;}}//交换方法private void swap(int parent,int child,int elem[]){int temp=elem[parent];elem[parent]=elem[child];elem[child]=temp;}

注解:这里孩子节点等于父亲节点的求法,小编就不再多说了,至于这里的外部循环,就是判断当交换后,需要再次进行二次判断,并且交换,每次交换后,父亲结点等于孩子结点的位置下标,然后再次求其孩子结点,实现向下判断,(那么此时的外部循环就是实现向下循环的条件)

2.4堆的插入 🌟🌟

1.思路 

       首先当堆满的时候要进行扩容,其次当我们插入时,一般在尾部进行插入,然后进行向上调整,又因为,向上调整是向下调整过后的堆,那么就只用看一条树,就是插入尾部的那条树。此时就不需要进行位置是否正确的再次判断。

2.代码实现
public int[] offer(int key){if (isFull()){this.elme=Arrays.copyOf(elme,elme.length*2);}elme[usedSize]=key;int child=usedSize;int parent=(usedSize-1)/2;while (true){if(elme[parent]<elme[child]){swap(parent,child,elme);}else {break;}child=parent;parent=(parent-1)/2;}return elme;}private boolean isFull(){       //判断堆是否满了return usedSize== elme.length;}

注解:这里小编在尾部插入元素后就进行父子结点与插入结点的判断并交换,然后每次交换,父亲与孩子的节点索引就要进行变化,若当父亲结点大于孩子结点,就直接跳出循环。

2.5堆的删除🌟🌟

1.思路

       这里小编认为删除堆顶元素,就将堆顶元素与末尾元素进行交换,然后有效数据减一,那么就不会对末尾元素进行操作,然后对前面的元素进行向下调整。

2.代码实现
 public int[] poll(){swap(0,usedSize-1,elme);usedSize--;downQueue(elme,0);return elme;}

📚️3.常用接口的介绍

3.1PriorityQueue的特性

Java集合框架中提供了PriorityQueuePriorityBlockingQueue两种类型的优先级队列PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全

• PriorityQueue中放置的元素必须要能够比较大小,不能插入无法比较大小的对象


• 不能插入null对象,否则会抛出NullPointerException


• 没有容量限制(取决于JVM内存管理,分配机制),可以插入任意多个元素,其内部可以自动扩容


• PriorityQueue底层使用了堆数据结构


• PriorityQueue默认情况下是小堆---即每次获取到的元素都是最小的元素

3.2PriorityQueue常用接口介绍 

PriorityQueue的构造方式:

代码如下:

// 创建一个空的优先级队列,默认容量11
PriorityQueue<Integer> q1 = new PriorityQueue<>();// 创建一个空的优先级队列,底层的容量为initialCapacity
PriorityQueue<Integer> q2 = new PriorityQueue<>(100);ArrayList<Integer> list = new ArrayList<>();
list.add(4);
list.add(3);
list.add(2);
list.add(1);
// 用ArrayList对象来构造一个优先级队列的对象
// q3中已经包含了4个元素
PriorityQueue<Integer> q3 = new PriorityQueue<>(list);
System.out.println(q3.size());

 PriorityQueue队列是小堆,如果需要大堆需要用户提供比较器

代码实例:

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

输出为5,那么就是完成了大堆的创建。

内部原码如下:

这里的if语句是实现比较的关键,当我们重写compare方法时,如果O2-O1<0那么就说明要进入位置交换实现大根堆,相反那么O1-O2>0那么就不交换,就实现小根堆。

3.3其他函数功能介绍

这里还有clear(),与isEmpty()代表清空与判断是否为空

代码实例:

 int[] arr = {4, 1, 9, 2, 8, 0, 7, 3, 6, 5};// 一般在创建优先级队列对象时,如果知道元素个数,建议就直接将底层容量给好
// 否则在插入时需要不多的扩容
// 扩容机制:开辟更大的空间,拷贝元素,这样效率会比较低PriorityQueue<Integer> q = new PriorityQueue<>(arr.length);
for(int e:arr){q.offer(e);}System.out.println(q.size()); // 打印优先级队列中有效元素个数System.out.println(q.peek()); // 获取优先级最高的元素
// 从优先级队列中删除两个元素之和,再次获取优先级最高的元素q.poll();q.poll();System.out.println(q.size()); // 打印优先级队列中有效元素个数System.out.println(q.peek()); // 获取优先级最高的元素q.offer(0);System.out.println(q.peek()); // 获取优先级最高的元素
// 将优先级队列中的有效元素删除掉,检测其是否为空q.clear();if(q.isEmpty()){System.out.println("优先级队列已经为空!!!")}else{system.out.println("不空")}

                                       🌅🌅🌅~~~~最后希望与诸君共勉,共同进步!!!

                               💪💪💪以上就是本期内容了, 感兴趣的话,就关注小编吧。

                                                         😊😊  期待你的关注~~~
————————————————     

 

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • ChatGLM 主要代码分析
  • 软件测试---接口测试
  • 设计模式(2)行为型模式和七大原则
  • 【Rust日报】通过Flutter实现Rust GUI库的开发
  • Linux基础入门---安装vmware
  • 精武杯的部分复现
  • 深度学习(9)---ResNet详解
  • 离线安装prometheus与Grafana实现可视化监控
  • C语言学习笔记 Day14(文件管理)
  • 用wordpress搭建网站的环境要求
  • 首款AI智能体IDE:LangGraph Studio
  • 网络接口 eno1 未连接或未托管
  • 【分立元件】贴片电阻器的故障现象和原理
  • 【Harmony OS 4.0】交互事件(手势事件)
  • 金价多次尝试刷新最高纪录,美国零售销售数据是绊马索
  • 3.7、@ResponseBody 和 @RestController
  • Django 博客开发教程 16 - 统计文章阅读量
  • download使用浅析
  • ReactNativeweexDeviceOne对比
  • ucore操作系统实验笔记 - 重新理解中断
  • Vue小说阅读器(仿追书神器)
  • 对话:中国为什么有前途/ 写给中国的经济学
  • 给初学者:JavaScript 中数组操作注意点
  • 前端技术周刊 2019-01-14:客户端存储
  • 前端设计模式
  • 设计模式(12)迭代器模式(讲解+应用)
  • 手写一个CommonJS打包工具(一)
  • 微服务核心架构梳理
  • 小程序 setData 学问多
  • 400多位云计算专家和开发者,加入了同一个组织 ...
  • ​浅谈 Linux 中的 core dump 分析方法
  • ​软考-高级-系统架构设计师教程(清华第2版)【第12章 信息系统架构设计理论与实践(P420~465)-思维导图】​
  • #微信小程序(布局、渲染层基础知识)
  • (2015)JS ES6 必知的十个 特性
  • (ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY)讲解
  • (附源码)springboot猪场管理系统 毕业设计 160901
  • (附源码)基于SSM多源异构数据关联技术构建智能校园-计算机毕设 64366
  • (计算机网络)物理层
  • (三)uboot源码分析
  • (循环依赖问题)学习spring的第九天
  • (一)硬件制作--从零开始自制linux掌上电脑(F1C200S) <嵌入式项目>
  • (转)LINQ之路
  • (转)linux自定义开机启动服务和chkconfig使用方法
  • (转)重识new
  • (最全解法)输入一个整数,输出该数二进制表示中1的个数。
  • .axf 转化 .bin文件 的方法
  • .config、Kconfig、***_defconfig之间的关系和工作原理
  • .mp4格式的视频为何不能通过video标签在chrome浏览器中播放?
  • .NET 实现 NTFS 文件系统的硬链接 mklink /J(Junction)
  • .NET 通过系统影子账户实现权限维持
  • .NetCore Flurl.Http 升级到4.0后 https 无法建立SSL连接
  • .NET开源快速、强大、免费的电子表格组件
  • [12] 使用 CUDA 加速排序算法
  • [2015][note]基于薄向列液晶层的可调谐THz fishnet超材料快速开关——
  • [2019/05/17]解决springboot测试List接口时JSON传参异常