用“堆”模拟实现“优先级队列”

在这里插入图片描述

PriorityQueue优先级队列

  • 1. 优先级队列的概念
  • 2. 优先队列的模拟实现
  • 3 堆的概念
  • 4. 堆的存储方式
  • 5. 堆向下调整
  • 6. 堆的创建
  • 7. 堆的插入
  • 8. 堆的删除
  • 9. 用堆模拟实现优先级队列

1. 优先级队列的概念

前面我们学习了队列,队列是一种“先进先出”的数据结构,但是在一些的特定情况下,我们需要将一些优先级高的数据先出队列,这时普通的队列显然是无法满足的,这时就引进了优先级队列

2. 优先队列的模拟实现

在JDK1.8中,PriorityQueue优先级队列底层使用的是堆这种数据结构,而堆本质上是一颗完全二叉树

3 堆的概念

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

4. 堆的存储方式

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

如果不是完全二叉树,则不适合采用该方式来存储,因为为了能够还原二叉树,空间中必须要存储空节点,就会导致空间利用率比较低

注意:

  • 堆在逻辑上,结构是一个完全二叉树;而在实际存储结构中,是存储在一个数组中的

以大根堆为例:
在这里插入图片描述
假设堆中某个节点在数组中的下标为i

  • 如果2i+1 < 数组长度(即节点个数),那么该节点存在左孩子,并且它的左孩子在数组中的下标为2i+1
  • 如果2i+2 < 数组长度(即节点个数),那么该节点存在右孩子,并且它的右孩子在数组中的下标为2i+2
  • 如果i不等于0,即该节点不是根节点,那么它的父亲节点是(i-1)/2

5. 堆向下调整

我们先考虑一种最简单的情况,假如一棵完全二叉树只有根节点不满足堆的概念,那么该怎么去调整这棵二叉树,使其成为一个堆呢?我们可以采用向下调整的方式

以调整为小根堆为例,对于下面这棵完全二叉树
在这里插入图片描述
观察该树,可知:根节点的左右子树都已经满足堆的性质了,只有根节点不满足,因此我们现在将根节点进行向下调整,步骤如下:
在这里插入图片描述
用代码实现的结果如下:

public void shiftDown(int[] array, int parent) {//childMin用来标记parent左右孩子的最小者//先用childMin标记parent的左孩子,因为parent可能有左孩子没有右孩子int childMin = 2 * parent + 1;//不断向下调整while(childMin < array.length) {//如果右孩子存在且左右孩子最小值为右孩子,则用childMin标记右孩子if(childMin + 1 < array.length && array[childMin] > array[childMin+1]) {childMin = childMin + 1;}if(array[parent] > array[childMin]) {//交换值int tmp = array[parent];array[parent] = array[childMin];array[childMin] = tmp;//交换值后,还需往后调整,因为交换值可能会破坏array[childMin]为根节点的堆结构parent = childMin;childMin = 2 * parent + 1;}else {//说明当前结构满足堆的性质break;}}}

注意: 在调整以parent为根的二叉树时,必须满足parent的左子树和右子树都已经是堆了,这样才能进行向下调整操作

时间复杂度分析: 最坏情况下,即parent一直向下调整到叶子节点,这时的时间复杂度就是树的高度,即O(log2N )

6. 堆的创建

如果对于一个普通序列,不是只有根节点不满足堆的性质,那么该怎么去调整呢?下面以序列{1,5,3,8,7,6}为例,创建一个大根堆

步骤如下:

在这里插入图片描述
代码如下:

    public void createHeap(int[] array) {// 找倒数第一个非叶子节点,从该节点位置开始往前一直到根节点,遇到一个节点,应用向下调整int root = ((array.length-2)>>1);for (; root >= 0; root--) {shiftDown(array, root);}}

使用向下调整的方式建堆,时间复杂度为:O(N)
使用向上调整的方式建堆,时间复杂度为:O(Nlog2N)

因此,在建堆时,使用向下建堆的效率更高

7. 堆的插入

在堆中插入元素都是将其放在数组的尾部(数组满了则需扩容),插入后可能会破坏堆的结构,这时就需要进行调整,需要从底部(插入元素的位置)开始往上调整,即向上调整,大致思路与向下调整一致

代码如下:

public void shiftUp(int child) {int parent = (child-1)/2;while(parent >= 0) {if(elem[parent] > elem[child]) {int tmp = elem[parent];elem[parent] = elem[child];elem[child] = tmp;child = parent;parent = (child-1)/2;}else {break;}}}

8. 堆的删除

注意:堆的删除,删除的是堆顶元素,即这棵完全二叉树的根节点,删除步骤如下:

  1. 将堆顶元素和最后一个元素进行交换
  2. usedSize–
  3. 将堆顶元素向下调整

9. 用堆模拟实现优先级队列

完整代码如下:

import java.util.Arrays;
public class PriorityQueue {public int[] elem;public int usedSize;public PriorityQueue() {this.elem = new int[10];}public void initElem(int[] array) {for (int i = 0; i < array.length; i++) {this.elem[i] = array[i];this.usedSize++;}}public void shiftUp(int child) {int parent = (child-1)/2;while(parent >= 0) {if(elem[parent] > elem[child]) {int tmp = elem[parent];elem[parent] = elem[child];elem[child] = tmp;child = parent;parent = (child-1)/2;}else {break;}}}public void shiftDown(int parent, int usedSize) {//childMin用来标记parent左右孩子的最小者//先用childMin标记parent的左孩子,因为parent可能有左孩子没有右孩子int childMin = 2 * parent + 1;//不断向下调整while(childMin < usedSize) {//如果右孩子存在且左右孩子最小值为右孩子,则用childMin标记右孩子if(childMin + 1 < usedSize && elem[childMin] > elem[childMin+1]) {childMin = childMin + 1;}if(elem[parent] > elem[childMin]) {//交换值int tmp = elem[parent];elem[parent] = elem[childMin];elem[childMin] = tmp;//交换值后,还需往后调整,因为交换值可能会破坏elem[childMin]为根节点的堆结构parent = childMin;childMin = 2 * parent + 1;}else {//说明当前结构满足堆的性质break;}}}public void createHeap() {// 找倒数第一个非叶子节点,从该节点位置开始往前一直到根节点,遇到一个节点,应用向下调整for (int parent = (this.usedSize-1-1)/2; parent >= 0 ; parent--) {shiftDown(parent,this.usedSize);}}public void offer(int val) {if(isFull()) {elem = Arrays.copyOf(elem, 2*elem.length);}elem[usedSize] = val;shiftUp(usedSize);usedSize++;}public boolean isFull() {return usedSize == elem.length;}public boolean isEmpty() {return usedSize == 0;}public int poll() {if(isEmpty()) {return -1;}int tmp = elem[0];elem[0] = elem[usedSize-1];elem[usedSize-1] = tmp;usedSize--;return tmp;}public int peek() {if(isEmpty()) {return -1;}return elem[0];}
}

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

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

相关文章

智慧农业大数据平台:智汇田园,数驭未来

智慧农业大数据平台 计讯物联智慧农业大数据平台是一个集管理数字化、作业自动化、生产智能化、产品绿色化、环境信息化、服务现代化于一体的多功能监管系统。它通过与硬件产品的搭配使用&#xff0c;实现对农业生产全过程的实时监测、精准控制和科学管理。该平台集成了多个数…

blender 小车建模 建模 学习笔记

一、学习blender视频教程链接 案例4&#xff1a;狂奔的小车_建模_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Bt4y1E7qn?p14&spm_id_from333.788.videopod.episodes&vd_sourced0ea58f1127eed138a4ba5421c577eb1 二、开始建模 &#xff08;1&#xff09;创…

逻辑回归与神经网络

从逻辑回归开始学习神经网络 神经网络直观上解释&#xff0c;就是由许多相互连接的圆圈组成的网络模型&#xff1a; 而逻辑回归可以看作是这个网络中的一个圆圈&#xff1a; 圆圈被称为神经元&#xff0c;整个网络被称为神经网络。 本节的任务是我们究竟如何理解具体的一个神…

华为OD机试 - 芯片资源占用(Java 2024 E卷 200分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;E卷D卷A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加…

QT仿QQ聊天项目,第一节,创建项目并布置编辑登录界面

目录 一&#xff0c;创建项目 二&#xff0c;编辑登录界面 1&#xff0c;登录界面整体构造 2&#xff0c;登录界面的宽高 3&#xff0c;登录界面使用到的控件 4&#xff0c;登录界面中的控件所在的位置和大小 &#xff08;1&#xff09;qq图标label位置和大小 &#xff0…

MySQL-事务隔离级别

1. MySQL事务的四种隔离级别 1.1 读未提交&#xff08;READ UNCOMMITTED&#xff09; READ UNCOMMITED提供了事务之间最小限度的隔离&#xff0c;除了幻读和不可重复读取的操作外&#xff0c;处于这个隔离级别的事务可以读到其它事务还未提交的数据。 1.2 读已提交&#xf…

哪个牌子的电容笔值得入手?!实测西圣、品胜、倍思三大热门品牌!

电容笔逐渐走入了大众视野&#xff0c;不仅数码博主人手一支&#xff0c;很多上班族和学生党也开始使用电容笔来进行无纸化办公和学习。然而&#xff0c;市场上的电容笔品牌众多&#xff0c;产品质量参差不齐&#xff0c;为了帮助大家挑选出真正优质的产品&#xff0c;我花费了…

传奇开服教程之新GOM引擎登录器配置教程

现在新GOM引擎的版本比以前多了一些&#xff0c;是时候和你们分享一期新GOM引擎登录器配置教程了&#xff0c;顺便来和你们分享下新GOM引擎和老GOM引擎的区别。 新GOM引擎与老GOM的区别 1、老GOM引擎1108的pak.txt就在登录器配置文件夹下&#xff0c;新GOM引擎的pak.txt在登录…

使用 ASP.NET Core 8.0 创建最小 API

构建最小 API&#xff0c;以创建具有最小依赖项的 HTTP API。 它们非常适合需要在 ASP.NET Core 中仅包括最少文件、功能和依赖项的微服务和应用。 本教程介绍使用 ASP.NET Core 生成最小 API 的基础知识。 在 ASP.NET Core 中创建 API 的另一种方法是使用控制器。 有关在最小 …

哪些CRM系统适合医疗行业?主流10款产品全解析

本文介绍了10款crm系统&#xff1a;纷享销客、Zoho CRM、海创CRM、红云CRM、慧影CRM、易华录CRM、用友健康CRM、Highrise CRM、Maximizer CRM、Infusionsoft by Keap。 在医疗行业中&#xff0c;选择合适的客户关系管理&#xff08;CRM&#xff09;系统可能是一项令人头疼的挑战…

Redis 哨兵 总结

前言 相关系列 《Redis & 目录》&#xff08;持续更新&#xff09;《Redis & 哨兵 & 源码》&#xff08;学习过程/多有漏误/仅作参考/不再更新&#xff09;《Redis & 哨兵 & 总结》&#xff08;学习总结/最新最准/持续更新&#xff09;《Redis & 哨兵…

学习笔记:黑马程序员JavaWeb开发教程(2024.10.26)

P3 Day01-02 需要记住&#xff1a; P4 Web前端开发 P34 Ajax介绍 对于异步交互的举例&#xff1a;浏览器中输入不同的关键词&#xff0c;会有不同的提示&#xff0c;但是浏览器没有进行刷新 同步&#xff0c;会进行等待&#xff0c;在浏览器中访问链接&#xff0c;点击网页什么…

keepalived+web 实现双机热备

环境&#xff1a;利用keeplived实现web服务器的双机热备(高可用) 注意&#xff1a; (1) 利用keeplivedweb做双击热备&#xff08;高可用&#xff09;&#xff0c;最少需要两台服务器&#xff0c;可以实现多域名对应一个VIP,并且访问不同域名&#xff0c;显示不同主页&#xf…

fetch: 取消请求、读取流、获取下载进度...

引言 Fetch API 提供了一个获取资源的接口(包括跨网络通信)。对于任何使用过 XMLHttpRequest 的开发者来说, 对于 Fetch 应该都能轻松上手, 而且新的 API 提供了更强大和灵活的功能集… 本文主要就是记录下, 在使用 Fetch 期间可能会碰到的几个小案例… 一、取消请求 在前端…

【动态规划】力扣509. 斐波那契数

目录 一、题目二、代码 一、题目 二、代码 class Solution {public int fib(int n) {if (n < 1) {return n;}int[] f new int[n 1];f[0] 0;f[1] 1;for (int i 2; i < n; i) {f[i] f[i - 1] f[i - 2];}return f[n];} }

从蚂蚁金服面试题窥探STW机制

背景 在Java虚拟机&#xff08;JVM&#xff09;中&#xff0c;垃圾回收&#xff08;GC&#xff09;是一个至关重要的机制&#xff0c;它负责自动管理内存的分配和释放。然而&#xff0c;垃圾回收过程并非没有代价&#xff0c;其中最为显著的一个影响就是STW&#xff08;Stop-T…

Flink CDC系列之:学习理解核心概念——Data Pipeline

Flink CDC系列之&#xff1a;学习理解核心概念——Data Pipeline 数据管道sourcesink管道配置Table IDroutetransform案例 数据管道 由于 Flink CDC 中的事件以管道方式从上游流向下游&#xff0c;因此整个 ETL 任务被称为数据管道。 管道对应于 Flink 中的一系列操作。 要描…

知识见闻 - 磁力片原理

磁力片是一种利用磁性原理设计的玩具&#xff0c;它的工作原理和磁性方向的排列方式非常有趣。让我们深入了解一下磁力片的核心原理和磁性方向的特点。 磁力片的基本原理 磁力片的工作原理基于磁铁的基本特性。每个磁力片都包含多个小磁铁&#xff0c;这些磁铁被精心排列&#…

初识Linux · 动静态库(incomplete)

目录 前言&#xff1a; 静态库 动态库 前言&#xff1a; 继上文&#xff0c;我们从磁盘的理解&#xff0c;到了文件系统框架的基本搭建&#xff0c;再到软硬链接部分&#xff0c;我们开始逐渐理解了为什么运行程序需要./a.out了&#xff0c;这个前面的.是什么我们也知道了。…

如何在 Linux 中对 USB 驱动器进行分区

如何在 Linux 中对 USB 驱动器进行分区 一、说明 为了在 Linux 上访问 USB 驱动器&#xff0c;它需要有一个或多个分区。由于 USB 驱动器通常相对较小&#xff0c;仅用于临时存储或轻松传输文件&#xff0c;因此绝大多数用户会选择只配置一个跨越整个 USB 磁盘的分区。但是&a…