数据结构与算法:优先级队列(堆)

news2024/11/18 21:29:54

1.优先级队列

1.定义

前面介绍过队列,队列是一种先进先出(FIFO)的数据结构,但有些情况下,操作的数据可能带有优先级,一般出队列时,可能需要优先级高的元素先出队列,该中场景下,使用队列显然不合适,比如:在手机上玩游戏的时候,如果有来电,那么系统应该优先处理打进来的电话;初中那会班主任排座位时可能会让成绩好的同学先挑座位。

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

2.堆

1.堆的概念

堆(Heap)是计算机科学中一类特殊的数据结构的统称。

堆通常是一个可以被看做一棵完全二叉树的数组对象。

堆满足下列性质:

  • 堆中某个节点的值总是不大于或不小于其父节点的值。
  • 堆总是一棵完全二叉树。

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

上图中,第一个图为大根堆,第二个图为小根堆。

2.存储方式

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

3.堆的创建

1.向下调整

我们以集合{ 27,15,19,18,28,34,65,49,25,37 }中的数据为例

在这里插入图片描述
仔细观察上图后发现:根节点的左右子树已经完全满足堆的性质,因此只需将根节点向下调整好即可

在这里插入图片描述
向下过程(以小堆为例):

  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。

在这里插入图片描述

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的左子树和右子树已经是堆了才可以向下调整。

最坏的情况即图示的情况,从根一路比较到叶子,比较的次数为完全二叉树的高度,即时间复杂度为O(logn)

那对于普通的序列{ 1,5,3,8,7,6 },即根节点的左右子树不满足堆的特性,则需要找倒数第一个非叶子节点,从该节点位置开始往前一直到根节点,遇到一个节点,应用向下调整

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

4.建堆的时间复杂度

在这里插入图片描述
利用错位相减法我们可以计算得知

在这里插入图片描述
建堆的时间复杂度为O(N)。

5.堆的插入

堆的插入总共需要两个步骤:

  1. 先将元素放入到底层空间中(注意:空间不够时需要扩容)。
  2. 将最后新插入的节点向上调整,直到满足堆的性质。

在这里插入图片描述

//堆插入元素(向上调整)
public void offer(int val) {
    if (isFull()) {
        //扩容
        elem = Arrays.copyOf(elem,2 * elem.length);
    }
    elem[usedSize++] = val;//11
    //向上调整
    shiftUp(usedSize - 1);
} 

public boolean isFull() {
    return usedSize == elem.length;
}

//向上调整建堆时间复杂度为N*logN
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;
        }
    }
}

6.堆的删除

注意:堆的删除一定删除的是堆顶元素。具体如下:

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

在这里插入图片描述

//堆插入删除元素(向下调整)
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[] arr,int i ,int j) {
    int tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
}

3.接口介绍

1.PriorityQueue的特性

Java集合框架中提供了PriorityQueue和PriorityBlockingQueue两种类型的优先级队列,PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的,本文主要介绍PriorityQueue。

注意事项:

  1. 使用时必须导入PriorityQueue所在的包,即:
import java.util.PriorityQueue
  1. PriorityQueue中放置的元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出ClassCastException异常
  2. 不能插入null对象,否则会抛出NullPointerException
  3. 没有容量限制,可以插入任意多个元素,其内部可以自动扩容
  4. 插入和删除元素的时间复杂度为O(logn)
  5. PriorityQueue底层使用了堆数据结构
  6. PriorityQueue默认情况下是小堆-–即每次获取到的元素都是最小的元素

2.2 PriorityQueue常用接口介绍

在这里插入图片描述

static void TestPriorityQueue() {
// 创建一个空的优先级队列,底层默认容量是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中已经包含了三个元素
    PriorityQueue<Integer> q3 = new PriorityQueue<>(list);
    System.out.println(q3.size());
    System.out.println(q3.peek());
}

注意:默认情况下,PriorityQueue队列是小堆,如果需要大堆需要用户提供比较器

// 用户自己定义的比较器:直接实现Comparator接口,然后重写该接口中的compare方法即可
class IntCmp implements Comparator<Integer> {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2 - o1;
    }
}
public class TestPriorityQueue {
    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());
    }
}

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

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

相关文章

【Rust】16. 智能指针

智能指针&#xff08;smart pointers&#xff09;是一类数据结构&#xff0c;他们的表现类似指针&#xff0c;但是也拥有额外的元数据和功能引用是一类只借用数据的指针&#xff1b;相反&#xff0c;在大部分情况下&#xff0c;智能指针拥有他们指向的数据 16.1 Box<T>&a…

1610_PC汇编语言_整形的表达

全部学习汇总&#xff1a; GreyZhang/g_unix: some basic learning about unix operating system. (github.com) 前面对于汇编有了一个基本的了解&#xff0c;这一章节主要是看一下汇编视角下的数据表达以及计算方式。 1. 整形会有有符号和无符号两种&#xff0c;一般都是用最高…

快上车,程序狗好用的奇淫技巧

文章目录前言&#x1f34a;缘由⏲️本文阅读时长&#x1f3af;主要目标正文&#x1f9d9;‍♂️1.魔术橡皮擦&#x1f415;2.狗屁不通文章生成器&#x1f95e;3.easypdf&#x1f97d;4.Md2All&#x1f32e;5.CSDN开发助手&#x1f468;‍&#x1f4bb;6.猿如意&#x1f9e9;7.P…

Three.js 初阶基础篇(二)

系列文章目录 我今天又来了更新了&#xff01;&#xff01;&#xff01;今天主要还是回顾一下昨天的一内容&#xff0c;在昨天的基础上又重新梳理了一下&#xff0c;创建动态3D正方体的流程&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 文章目录…

VMware虚拟机安装Linux教程

一、centos7下载 1、centos7的下载 官网下载地址: https://www.centos.org/download/ 2、点击x86_64 二、虚拟机下载与安装 1、VMware虚拟机下载 官网地址: https://www.vmware.com/cn/products/workstation-player.html 打开下载好的.exe文件 安装位置默认在 C 盘下…

【JavaSE】运算符

运算符BIT-3 运算符1. 什么是运算符2. 算数运算符2.1 基本四则运算符2.2 增量运算符2.3 自增/自减运算符3. 关系运算符4. 逻辑运算符&#xff08;重点&#xff09;4.1 逻辑与&&4.2 逻辑或||4.3 逻辑非&#xff01;4.4 短路求值5. 位运算符5.1 按位与&5.2 按位或|5.…

【借助pf4j实现基于spring-boot的插件化开发

借助pf4j实现基于spring-boot的插件化开发背景场景项目地址&结构Demo运行背景 Jenkins、SonarQube等Java实现的Web应用都有插件体系&#xff0c;一般来说都是先从插件市场下载一个插件&#xff0c;然后系统要求重启(某些功能可以不用重启)&#xff0c;插件功能就能在页面上…

SRM-供应商管理系统搭建指南

1、简介1.1、案例简介本文将介绍&#xff0c;如何搭建SRM-供应商管理。1.2、应用场景供应商可注册、提交、修改自己的基本信息及工商信息&#xff0c;上传资质档案、管理产品及样品信息&#xff1b;企业对供应商是否成为合格供应商或淘汰供应商进行准入流程的审批。2、设置方法…

软件设计(四)--MTBF、MTTF、MTTR

软件设计&#xff08;三&#xff09;https://blog.csdn.net/ke1ying/article/details/128808681 31、结构化开发中&#xff0c;过程设计 主要包含对数据结构和算法的设计。 体系结构设计&#xff1a;定义软件的主要结构元素及其关系。 数据设计&#xff1a;基于实体联系图&a…

高压放大器在多波长干涉相位同步解调方法研究中的应用

实验名称&#xff1a;基于激光相位分立调制的多波长干涉相位同步解调方法研究 研究方向&#xff1a;激光测量 测试目的&#xff1a; 在长度测量中&#xff0c;绝对距离测量&#xff08;ADM&#xff09;可实现高精度、大范围和瞬时距离测量&#xff0c;与相对位移测量&#xff0…

Dynamsoft Barcode Reader 9.6.1 for NET Crack

Dynamsoft Barcode Reader以每分钟 500 的速度扫描条码 Dynamsoft Barcode Reader具有灵活 API 的强大条码扫描器 SDK Dynamsoft Barcode Reader无论它是模糊的、扭曲的、黑暗的、遥远的还是移动的&#xff0c;我们都可以扫描它。快速地。 业界最快扫描解码&#xff0c;适用于…

一次线上超时问题,看Ribbon 超时机制

一、线上问题 项目上线后&#xff0c;一段时间内运行都没有问题&#xff0c;突然运营人员说&#xff0c;某个接口一直失败。遂查看线上日志&#xff1a; spring-cloud-openfeign 通过服务名调用的&#xff0c;服务发现没有找到可用服务实例&#xff1f;这是第一反应&#xff0…

如何添加PDF书签?学会这三种方法

随着PDF文档和书籍已经成为我们工作和生活中不可或缺的一部分&#xff0c;许多PDF书签制作器和编辑器应运而生。了解使用 Windows、Mac 兼容或在线软件免费将书签添加到 PDF的多种方法。 什么是 PDF 书签&#xff1f; PDF 文件中的书签与印刷书籍中的纸质或织物书签的工作方式…

1609_PC汇编语言_汇编简介以及first例程后续

全部学习汇总&#xff1a; GreyZhang/g_unix: some basic learning about unix operating system. (github.com) 看了一下&#xff0c;当前看的这个章节还剩下几页没有看完。看完之后&#xff0c;做一个简单的整理。 在上一篇笔记中我提到了关于DJGPP的疑惑&#xff0c;也自己找…

RocketMQ源码-NameServer架构设计及启动流程

本文我们来分析NameServer相关代码&#xff0c;在正式分析源码前&#xff0c;我们先来回忆下NameServer的功能&#xff1a; NameServer是一个非常简单的Topic路由注册中心&#xff0c;其角色类似Dubbo中的zookeeper&#xff0c;支持Broker的动态注册与发现。主要包括两个功能&…

【每日阅读】前端进阶知识点1

如何更改网页中语言属性值 声明当前语言类 html标签更改属性值 lang属性中不区分大小写 en-us en-US 一致 具体可使用 window,document.querySelector(“html”)?.setAttribute(“lang”,newValue); qs库 qs是一个流行的查询参数序列化和解析库。可以将一个普通的object序列…

09_FreeRTOS任务调度器

目录 开启任务调度器vTaskStartScheduler函数 xPortStartScheduler开启任务调度器函数 启动第一个任务 prvStartFirstTask开启第一个任务函数 vPortSVCHandler SVC中断服务函数 出栈/压栈汇编指令详解 开启任务调度器vTaskStartScheduler函数 作用:用于启动任务调度器,…

LaoCat带你认识容器与镜像(实践篇一上)

实践篇主要以各容器的挂载和附加命令为主。 本章内容 Dockerfile基础命令详解。 本文实操全部基于Ubuntu 20.04 宿主机 > linux服务器本身 Docker > 20.10.22 从该章开始&#xff0c;我们就进入了Docker实践篇系列了&#xff0c;主要介绍Docker相关附加命令&#xff0c;…

Python中的字符串多样的操作方法

文章目录1.字符串中大小写字母的转变2.字符串的左右中对齐3.字符串查找的方法4.字符串的替换5. 字符串的判断6.字符串的截取7.字符串的拆分8.字符串的拼接9.格式化字符串10.格式化字符串的语法10.12.1 对齐选项&#xff08;[align]&#xff09;10.2 填充选项&#xff08;[fill]…

切入点(pointcut)的申明规则

Spring AOP 用户可能会经常使用 execution切入点指示符。执行表达式的格式如下&#xff1a;execution&#xff08;modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern&#xff08;param-pattern&#xff09; throws-pattern?&#xff09;ret-type-p…