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

news2024/11/23 6:42:02

目录

一、优先级队列

1.1 堆

1.2 PriorityQueue接口

二、模拟实现优先级队列

2.1 初始化

2.2 创建大根堆 (向下调整)

2.3 堆的插入

2.4 堆的删除

2.5 堆排序

总结


一、优先级队列

优先级队列是一种特殊的队列,其出队顺序与入队顺序无关,而与优先级相关。其常见的实现方式就是使用作为底层数据结构。

1.1 堆

堆将所有元素按完全二叉树顺序存储方式存储在一个一维数组中,堆还分为大根堆小根堆

大根堆:每个结点的值都大于其子结点的值,最大值总是在堆顶。

小根堆:每个结点的值都小于其子结点的值,最小值总是在堆顶。

【完全二叉树的性质】

一棵有 n 个结点的完全二叉树,对于其编号为 i 的结点有:

  • 若 i > 0,父结点编号:[(i-1) / 2];若 i = 0,则 i 为根结点编号,无父结点。
  • 若 2i+1 < n,左孩子编号:2i+1;反之无左孩子。
  • 若 2i+2 < n,右孩子编号:2i+2;反之无右孩子。

1.2 PriorityQueue接口

在 Java 中提供了 PriorityQueue 和 PriorityBlockingQueue 接口来表示优先级队列,其中 PriorityQueue 线程不安全的,PriorityBlockingQueue 是线程安全的。

【PriorityQueue 的性质】

  • PriorityQueue 中放置的元素必须能够比较大小,否则会抛 ClassCastException。
  • 不能插入 null 对象,否则会抛 NullPointerException。
  • 没有容量限制,可以任意插入元素,其内部会自动扩容。
  • 插入和删除元素的时间复杂度为 O(log_{2}N)
  • PriorityQueue 底层使用了数据结构,默认情况下是小根堆

【构造方法】

方法说明
PriorityQueue()创建一个空的优先级队列,默认容量是11
PriorityQueue(int initialCapacity)创建一个初始容量为 initialCapacity (不能小于1) 的优先级队列
PriorityQueue(Collection<? extends E> c)用一个集合来创建优先级队列

【操作方法】 

方法说明
boolean offer(E e)插入元素 e
E poll()移除优先级最高的元素并返回
E peek()获取优先级最高的元素
int size()获取优先级队列中有效元素个数
boolean isEmpty()判断优先级队列是否为空
void clear()清空

二、模拟实现优先级队列

2.1 初始化

public class MyHeap {
    public int[] element;//定义数组以实现优先级队列(堆)
    public int usedSize;//记录堆中有效元素个数
    
    public MyHeap() {
        this.element = new int[10];
    }

    //初始化 element 数组
    public void initElement(int[] array) {
        for (int i = 0; i < array.length; i++) {
            //存入元素
            element[i] = array[i];
            //记录元素个数
            usedSize++;
        }
    }
}

2.2 创建大根堆 (向下调整)

① 初始令 parent 引用指向最后一棵子树的根结点进行调整,调整完一棵子树后,令 parent-- 前往其上一棵子树进行调整,直至调整完下标为 0 的根结点及其子树。

② 首先确保该根结点有左孩子的情况下,进入向下调整的过程,先令 child 引用指向孩子结点最大值

再比较 parent 与 child 大小,若 child 比 parent 大,则交换,交换后 parent 引用指向当前 child 引用,child 引用指向 parent 左孩子结点开始调整其下方子树,直至其下方不存在子树;

反之无需交换,直接结束循环。

    //创建大根堆:采用向下调整策略
    public void createHeap() {
        //从最后一棵子树开始调整,依次往前,直至根结点
        //父亲结点 = (孩子结点-1) / 2
        //usedSize-1 是树中最后一个结点
        for (int parent = (usedSize-1-1) / 2; parent >= 0; parent--) {
            //向下调整
            siftDown(parent,usedSize);
        }
    }

    //向下调整
    private void siftDown(int parent, int length) {
        //左孩子结点 = 父亲结点*2 + 1
        int child = parent*2 + 1;

        //首先保证该结点有左孩子结点
        while (child < length) {

            //确定该结点有右孩子结点,再进行比较
            //保证 child 引用指向孩子结点中最大值
            if ((child+1) < length && element[child] < element[child+1]) {
                //若右孩子的值大于左孩子,则 child 引用指向右孩子
                child = child + 1;
            }

            if (element[child] > element[parent]) {
                //若 child 比 parent 大,则交换元素
                swap(child, parent);

                //parent 指向 child 位置,向下调整至下方无子树
                parent = child;
                child = parent*2 + 1;
            } else {
                //子结点都比父结点小
                break;
            }
        }
    }

    //交换元素
    private void swap(int i, int j) {
        int tmp = element[i];
        element[i] = element[j];
        element[j] = tmp;
    }

2.3 堆的插入

将 value 存放至最后一个结点,开始向上调整。

② 在向上调整的过程中,只需比较 value 与当前的父亲结点大小,若需要交换,交换后 child 指向 parent,parent 指向 child 父结点;反之调整结束。

    //入堆
    public void offer(int value) {
        if (isFull()) {
            element = Arrays.copyOf(element, 2*element.length);
        }
        //将 value 放至最后一个结点
        element[usedSize] = value;
        //向上调整
        siftUp(usedSize);

        usedSize++;
    }

    private boolean isFull() {
        return usedSize == element.length;
    }

    //向上调整
    public void siftUp(int child) {
        //value 的父亲结点
        int parent = (child-1) / 2;

        //调整至 child 指向下标为 0 的根结点
        while (child > 0) {
            //比较两者大小
            if (element[child] > element[parent]) {
                swap(child, parent);
                //交换后,child 指向 parent,parent 指向 child 父结点
                child = parent;
                parent = (child-1) / 2;
            } else {
                break;
            }
        }
    }

2.4 堆的删除

核心:将堆顶结点与最后一个结点交换,usedSize-- 实现出堆操作,最后向下调整为大根堆。

    //出堆
    public int poll() {
        //判空
        if (isEmpty()) {
            throw new EmptyException("堆为空!");
        }
        //记录堆顶元素
        int old = element[0];
        //堆顶结点与最后一个结点交换
        swap(0, usedSize-1);
        //删除原堆顶元素
        usedSize--;
        //出堆后开始向下调整为大根堆
        siftDown(0, usedSize);
        //返回堆顶元素
        return old;
    }

    private boolean isEmpty() {
        return usedSize == 0;
    }

2.5 堆排序

① 创建大根堆,初始化 end = userSzie-1,即 end 指向最后一个结点;

② 将栈顶元素交换至 end 下标。由于大根堆中栈顶元素最大,故交换一次,end--,保证每次交换后的栈顶元素位置不变;

③ 重新向下调整为大根堆;

④ 重复 ②、③ 操作,直至排序完成。

    //堆排序
    public void heapSort() {
        int end = usedSize-1;
        while (end > 0) {
            //将大根堆中栈顶元素交换至 end
            swap(0, end);
            //向下调整为大根堆
            siftDown(0, end);
            //保证每次调整的栈顶元素位置不变
            end--;
        }
    }

总结

1、优先级队列出队顺序与入队顺序无关,而与优先级相关。

2、堆将所有元素按完全二叉树的顺序存储方式存储在数组中。

3、堆分为大根堆和小根堆。

4、PriorityQueue 中放置的元素必须能够比较大小、不能插入 null 对象、没有容量限制。

5、PriorityQueue 默认情况下是小根堆,大根堆需要自行提供比较器。

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

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

相关文章

如何清空Comfyui的gpu缓存

由于我电脑上同时装了两个Comfyui作为我站点的绘图服务&#xff0c;一个是给正式服使用&#xff0c;一个是开发测试使用&#xff0c;在使用过程中经常会因为两个Comfyui服务跑图后没有自动释放显存导致爆显存。所以我需要让Comfyui跑完图之后可以自动释放显存。 我自己在网上找…

Python的Django部署uwsgi后自签名实现的HTTPS

通过SSL/TLS来加密和客户端的通信内容。提高网络安全性&#xff0c;但是会损耗部分的服务器资源。 HTTPS 的原理图。 web.key 是打死也不能给其他人的。一定要保存好。里面主要是私钥。是各种认证的根基。本地测试的话生成1024的即可&#xff0c;如果是生产环境推荐使用2048。…

高考后的抉择:专业优先还是学校优先?

随着2024年高考的帷幕落下&#xff0c;高考生们面临的一个重要抉择再度浮上心头&#xff1a;在分数受限的情况下&#xff0c;是选择一个心仪的专业&#xff0c;还是选择一个知名度更高的学校&#xff1f;这是一个困扰了众多考生和家长的长期难题。在这个关键的时刻&#xff0c;…

Vip-智能预估+大数据标签+人群全选=用户分群!

Mobpush用户分群功能升级&#xff0c;创建推送入口vip用户可进入自有选择标签创建“用户分群”&#xff0c;相比于免费标签&#xff0c;“用户标签”维度更丰富。在应用基础属性上&#xff0c;增加“品牌”、“网络状态”、“运营商”&#xff0c;众所周知&#xff0c;不同厂商…

公文出错事非小,这些公文写作的常见错误,你中过招吗?

公文是企事业单位、相关部门内外沟通交流的重要工具&#xff0c;不少“笔杆子”经常需要与公文打交道&#xff0c;每天会接触大量的公文。然而在公文撰写的细微之处&#xff0c;稍有不慎&#xff0c;便可能犯下一些常见的错误。这些错误如同蚁穴&#xff0c;虽小却足以破坏公文…

图片管理大革新:一键批量转换、美化与底片效果制作,轻松打造专业级图片库!

在数字时代&#xff0c;图片已成为我们生活、工作中不可或缺的一部分。然而&#xff0c;随着图片数量的不断增加&#xff0c;如何高效、便捷地管理、转换和美化这些图片&#xff0c;成为了许多人头疼的问题。今天&#xff0c;我们向您推荐一款全新的图片批量管理工具&#xff0…

边缘计算盒子是什么、有哪些作用?如何选型?这篇文章全面帮你了解!边缘计算云服务器ECS

一、边缘盒子是什么&#xff1f; 近年&#xff0c;边缘盒子被大家所了解&#xff0c;那么边缘盒子到底是什么呢&#xff1f; 边缘计算盒子内部配置了丰富的 AI 算法&#xff0c;这些算法可以帮助用户在很多场景下实现数据识别等作用。比如&#xff0c;在工地场景下安装边缘盒…

Nvidia jetson Orin/RK3588+AI仿生事件相机

EVS 事件相机 速度&#xff0c;性能&#xff0c;效率和安全&#xff0c;为自动驾驶、自动化和高速运动带来新的高度 采用SONY IMX646 EVS图像传感器&#xff0c;基于PROPHESEE METAVISION 技术 高帧率&#xff0c;120dB高动态范围 实现高速和高时间分辨率。现在已经没有帧率的概…

po文件并转换成mo文件

po文件转换成mo文件 简介 .po和.mo文件是WordPress中语言相关的两种文件。po 是Portable Object(可移植对象)的缩写&#xff0c;存放待翻译的字符串信息&#xff0c;可直接用文本编辑器打开编辑&#xff1b;mo 是Machine Object的缩写&#xff0c;二进制文件&#xff0c;程序…

SmartEDA电路仿真教学VS传统教学:谁将颠覆未来教育战场?

在科技飞速发展的今天&#xff0c;教育领域也迎来了前所未有的变革。传统的教学方式正面临着在线教学工具的强劲挑战&#xff0c;特别是在电路仿真教学领域&#xff0c;SmartEDA这样的在线工具正逐渐崭露头角。那么&#xff0c;SmartEDA电路仿真教学与传统教学之间&#xff0c;…

超支化聚合物(HBPs)应用潜力巨大 我国研究、产业化及应用取得了显著进展

超支化聚合物&#xff08;HBPs&#xff09;应用潜力巨大 我国研究、产业化及应用取得了显著进展 超支化聚合物简称HBPs&#xff0c;是一类高度支化的三维拓扑高分子&#xff0c;由沿聚合物骨架随机分布的树枝状单元、线性单元、末端单元组成。超支化聚合物结构独特&#xff0c;…

一名HR,在招聘嵌入式开发岗位,为什么感觉一年比一年难?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「嵌入式的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01; 1.嵌入式学用不一致, 高…

【星海随笔】vue+vite

开头问一个问题&#xff0c;我发现有的人粉丝是点赞数的好几倍&#xff0c;可能和必须关注他才能阅读他的文章有关。 所以问一下怎么设置为关注才能查看该文章。 screen -ls #查看 id 列表 screen -S <session_name> # 创建一个会话 screen -R <session_id> # 根据…

高考填报志愿三连问,从人格优势分析兴趣和专业

“我的兴趣爱好什么&#xff1f;” “我的理想是什么&#xff1f;” “我想成为什么&#xff1f;” ------高考填报志愿三连问&#xff01; 最近我在知乎上看过一个比较有意义的提问&#xff0c;提问的也是高考填报志愿的同学&#xff0c;自从高考后&#xff0c;每日三连问&…

笔记本电脑升级实战手册[1]:开始之前的准备与清单

文章目录 前言&#xff1a;一、升级流程1. 备份2. 清灰换硅脂3. 扩展内存与硬盘4. 硬盘设置5. 系统重装6. 升级后性能测试 二、升级清单1. 工具清单2. 升级清单 总结&#xff1a; 前言&#xff1a; 将要毕业之际&#xff0c;发现我的笔记本电脑已经陪我“征战沙场”快有四年之…

iOS政策解读之三丨商务、设计和法律 “三重奏“

上一篇的iOS政策解读文章&#xff0c;我们从安全和性能两方面进行了学习和解读&#xff0c;这两个方面是最为重要&#xff0c;也是优先级最高的方面。 如果您还没来得及阅读&#xff0c;欢迎移步我们前两篇的解读文章&#xff1a; iOS政策解读之一丨App提交审核前注意事项必知…

3d怎么把歪的模型摆正?---模大狮模型网

在进行3D建模过程中&#xff0c;有时候会遇到模型出现歪曲或者旋转不正确的情况&#xff0c;这可能会影响到后续的设计和渲染效果。因此&#xff0c;学会将歪曲的模型摆正是一个非常重要的技巧。模大狮将介绍几种常用的方法&#xff0c;帮助您有效地将歪曲的3D模型摆正&#xf…

有什么简单易上手的CRM系统推荐?五款CRM软件评测

在数字化时代&#xff0c;企业急需一个能全面展示客户、销售和分析数据的CRM系统。当然&#xff0c;简单易用的CRM系统成了企业首选。选择系统时&#xff0c;同时要关注它的实际功能是否满足需求&#xff0c;是否容易上手&#xff0c;能否根据企业需求灵活定制&#xff0c;能否…

MyBatisX插件生成MyBatis Plus代码

MyBatis-Plus为我们提供了强大的mapper和service模板&#xff0c;能够大大的提高开发效率 但是在真正开发过程中&#xff0c;MyBatis-Plus并不能为我们解决所有问题&#xff0c;例如一些复杂的SQL&#xff0c;多表 联查&#xff0c;我们就需要自己去编写代码和SQL语句&#xf…

SVN 的忽略(Ignore)和递归(Recursively)以及忽略部分

SVN中忽略大家经常用到&#xff0c;但总是似懂非懂&#xff0c;下面就详细展开说明一下忽略如何设置。 两个忽略 通常设置忽略都是文件夹和里面的文件都忽略。 设置忽略我们通常只需要鼠标右键点击忽略就可以了&#xff0c;如图&#xff1a; 第一个忽略用的最多&#xff0c;…