数据结构-堆和PriorityQueue

1.堆(Heap)

1.1堆的概念

堆是一种非常重要的数据结构,通常被实现为一种特殊的完全二叉树

如果有一个关键码的集合K={k0,k1,k2,...,kn-1},把它所有的元素按照完全二叉树的顺序存储在一个一维数组中,如果满足ki<=k2i+1且ki<=k2i+2(i=0,1,2,3...),则称这个集合为小堆;如果满足ki>=k2i+1且ki>=k2i+2(i=0,1,2,3...),则称这个集合为大堆。

简单来说,根节点的值为最大的堆叫做最大堆或大根堆,根节点的值最小的堆叫做最小堆或小根堆

1.2堆的性质

1.完全二叉树的性质

除了最后一层外,每一层都被填满,最后一层的节点从左往后填充

2.堆序性质

最大堆(大根堆):

对于每个节点i,其左子节点2i+1和右子节点2i+2的值都小于或等于i的值

最小堆(小根堆):

对于每个节点i,其左子节点2i+1和右子节点2i+2的值都大于或等于i的值

1.3堆的存储方式

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

注意: 对于非完全二叉树来说,不适合使用顺序的方式进行存储,因为为了还原二叉树,空间中必须要存储空节点,就会导致空间利用率比较低

1.4堆的创建

给定一个集合{28,16,20,19,29,35,66,50,26,38},如何将其创建为堆呢?

观察发现:根节点的左右子树都已经满足堆的性质,因此只需要将根节点向下调整即可 

1.4.1堆的向下调整

1.用parent表示要调整的节点,child表示parent的左孩子(注意:堆是一颗完全二叉树,如果parent有孩子一定是先有左孩子

2.如果parent的左孩子存在,即child<size,进行如下操作,直到parent的左孩子不存在:

       parent的右孩子是否存在,如果存在,则找到左右孩子中最小的元素,让child表示这个元素。

       将parent与较小的孩子child进行比较,如果parent小于child,调整结束。

       如果parent大于child,将parent和child进行交换,原来parent中较大的元素向下调整可能会导         致子树不满足堆的性质,因此要继续向下调整,即parent=child,child=2*parent+1,继续进           行步骤2

代码编写:

    public void shiftDown(int[] array,int parent){
        int child=2*parent+1;
        int size=array.length;
        while(child<size){
            //如果右孩子存在,用child标记左右孩子中较小的值
            if(child+1<size&&array[child+1]<array[child]){
                child++;
            }
            if(array[parent]>array[child]){
                swap(array,parent,child);
                //继续向下调整,为了保证子树也满足堆的性质
                parent=child;
                child=2*parent+1;
            }else{
                break;
            }
        }
    }
    private void swap(int[] array,int a,int b){
        int tmp=array[a];
        array[a]=array[b];
        array[b]=tmp;
    }

 注意:再调整以parent为根的二叉树时,必须满足parent的左子树和右子树已经时堆了才可以进行向下调整。

1.4.2堆的创建(小根堆)

那么对于普通的序列,即根节点的左右子树不满足堆的性质,又该如何创建呢?

例如对普通序列{2,7,8,5,4,1}进行小根堆的创建

根据上面的堆的向下调整,我们的思路就是要将根的左右子树都满足小根堆的特点,我们可以从下向上,从最后一个非叶子节点出发,将其与他们的左右孩子进行比较,将最小的值与非叶子节点进行交换(堆的向下调整),再继续向上执行上述操作,直到操作的节点为根节点即可

代码编写:

     public void createSmallestHeap(int [] array){
        int root=(array.length-2)>>1;
        for(;root>=0;root--){
            shiftDown(array,root);
        }
    }

 

1.5堆的插入和删除

1.5.1堆的插入

堆的插入总共需要2步:

1.先将元素放入底层(空间不够时,需要进行扩容)

2.将新插入的元素不断向上调整,直到满足堆的性质

观察可以发现:如果新插入的节点的父节点大于新插入的节点,就进行元素的交换,不断重复该动作

代码编写:

    //child表示新插入元素的索引
    public void shiftUp(int child){
        //找到新插入节点的父节点
        int parent=(child-1)/2;
        while(child>0){
            if(array[parent]>array[child]){
                swap(array,parent,child);
                child=parent;
                parent=(child-1)/2;
            }else {
                break;
            }
        }
    }
1.5.2堆的删除

堆在删除过程中需要注意删除的元素一定是堆顶元素

1.将堆顶元素和堆中的左后一个元素交换位置

2.将堆中有效个数减少一个

3.对堆顶元素进行向下调整

代码编写:

     public void shiftDown(int[] array,int parent){
        int child=2*parent+1;
        int size=array.length;
        while(child<size){
            //如果右孩子存在,用child标记左右孩子中较小的值
            if(child+1<size&&array[child+1]<array[child]){
                child++;
            }
            if(array[parent]>array[child]){
                swap(array,parent,child);
                //继续向下调整,为了保证子树也满足堆的性质
                parent=child;
                child=2*parent+1;
            }else{
                break;
            }
        }
    }
    public void delete(int[] array){
        swap(array,0,size-1);
        //size表示有效元素的个数
        size--;
        shiftDown(array,0);
    }

 

1.6堆的应用

1.堆排序(Heap Sort)

利用堆的性质对数组进行排序,时间复杂度为O(nlogn)

2.优先级队列(PriorityQueue)

堆是实现优先级队列的高校数据结构,支持快速的插入和删除操作

3.Dijkstra算法

在最短路径算法中,堆用于高效地选择当前距离最小的节点

4.Kth Largest Element

也叫topK问题,使用堆可以高效地找到数组中的第k大元素

2.PriorityQueue

2.1什么是优先级队列

通过之前的介绍,队列是一种先进先出(FIFO)的数据结构,但是优先情况下,操作的数据可能带有优先级,并不希望按照队列原始的顺序进行出栈,可能优先级高的元素想先出队列

在生活中有一个很常见的例子:当你在用听歌的时候,突然接到电话,音乐会自动停止,而执行通话的操作

优先级队列(PriorityQueue)是一种特殊的队列,其中的每个元素都有一个优先级,队列会根据优先级来决定元素的出队顺序,优先级高的元素先出队,优先级低的元素后出队,如果两个元素的优先级相同,则按照它们入队列的顺序出队

优先级队列通常基于堆这种数据结构实现,因为堆可以高效地进行插入和删除操作,同时保持元素的优先级顺序

Java集合框架中提供了PriorityQueue和PriorityBlockingQueue两种类型的优先级队列,PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的,我们主要进行讲解PriorityQueue

2.2PriorityQueue常见操作

PriorityQueue的常见方法:

方法解释
PriorityQueue()创建一个空的优先级队列,默认容量是11
PriorityQueue(int initialCapacity)创建一个初始容量为initialCapacity的优先级队列,注意:initialCapacity不能小于1,否则会抛出IllegalArgumentException异常
PriorityQueue(Collection<? extends E> c)用一个集合来创建优先级队列
boolean offer(E e)插入元素e,插入成功返回true,如果e对象为空,则会抛出NullPointerException异常,空间不够的时候会进行扩容
E peek()获取优先级最高的元素,如果优先级队列为空,返回null
E poll()移除优先级最高的元素,如果优先级队列为空,返回null
int size()获取有效元素的个数
void clear()清空队列
boolean isEmpty()检测优先级队列是否为空,如果为空返回true

 我们以一个复杂的类型来演示:

public class Student implements Comparable<Student>{
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(Student o) {
        return this.age-o.age;
    }
}

public class Test {
    public static void main(String[] args) {
        PriorityQueue<Student> queue=new PriorityQueue<>();
        Student s1=new Student("hajimi",21);
        Student s2=new Student("king",18);
        queue.offer(s1);
        queue.offer(s2);
        System.out.println(queue.size());
        for(Student s:queue){
            System.out.println(s);
        }
        queue.clear();
        System.out.println(queue.size());
    }
}

 

2.3优先级队列的特性

因为优先级队列是基于堆实现的,所以优先级队列的操作的时间复杂度其实就是堆在操作过程中的时间复杂度

1.插入:

将一个新元素插入到队列中,并根据优先级调整队列的顺序,时间复杂度为O(logn)

2.删除:

删除优先级最高的元素,时间复杂度为O(logn)

3.获取优先级最高的元素:

返回优先级最高的元素,但不删除它,时间复杂度为O(1)

4.更新优先级:

更新队列中某个元素的优先级,并调整队列的顺序,时间复杂度为O(logn)

5.Priority中放置的元素必须是能够比较大小的,不能插入无法比较大小的对象,否则会抛出ClassCastException异常

6.不能插入null,否则会抛出NullPointerException

7.PriorityQueue默认情况下是小堆

8.PriorityQueue其内部可以自动扩容

2.4PriorityQueue底层的扩容原理

PriorityQueue的 默认无参构造方法创建的数组长度为11

如果容量小于64时,是按照oldCapacity的2倍方式扩容的

如果容量大于64时,是按照oldCapacity的1.5倍方式扩容的

如果容量超过MAX_ARRAY_SIZE,按照MAX_ARRAY_SIZE来进行扩容

2.5实现一个优先级队列

package datastructure;

import java.util.Arrays;

public class PriorityQueue {
    private int elem[];
    //队列中有效元素的个数
    private int usedSize;
    private final static int DEFAULT_INIT_SIZE=11;

    public PriorityQueue(){
        elem=new int[DEFAULT_INIT_SIZE];
    }
    public PriorityQueue(int[] elem){
        this.elem=elem;
        this.usedSize=elem.length;
        int root=((elem.length-2)>>1);
        for(;root>=0;root--){
            shiftDown(root,elem.length);
        }
    }
    private void shiftDown(int parent,int len){
        int child=parent*2+1;
        while (child<len){
            if(child+1<len&&elem[child+1]<elem[child]){
                child++;
            }
            if(elem[parent]>elem[child]){
                swap(elem,parent,child);
            }else {
                break;
            }
        }
    }
    public boolean offer(int value){
        if(usedSize==elem.length){
            Arrays.copyOf(elem,2*elem.length);
        }
        elem[usedSize]=value;
        usedSize++;
        shiftUp(usedSize-1);
        return true;
    }
    private void swap(int[] elem,int a,int b){
        int tmp=elem[a];
        elem[a]=elem[b];
        elem[b]=tmp;
    }
    private void shiftUp(int child){
        int parent=(child-1)/2;
        while (child>0){
            if(elem[child]<elem[parent]){
                swap(elem,parent,child);
                child=parent;
                parent=(child-1)/2;
            }else {
                break;
            }
        }
    }
    public int size(){
        return usedSize;
    }
    public int peek(){
        if(usedSize==0){
            System.out.println("优先级队列中没有元素,无法获取元素");
            return -1;
        }
        return elem[0];
    }
    public boolean isEmpty(){
        return usedSize==0;
    }
    public int poll(){
        if(usedSize==0){
            System.out.println("优先级队列中没有元素,无法删除元素");
            return -1;
        }
        int value=elem[0];
        swap(elem,0,usedSize-1);
        usedSize--;
        shiftDown(0,usedSize-1);
        return value;
    }
}

对编写的代码进行运行测试:

public class Test {
    public static void main(String[] args) {
        PriorityQueue queue=new PriorityQueue();
        queue.offer(2);
        queue.offer(4);
        queue.offer(3);
        queue.offer(8);
        queue.offer(7);
        queue.offer(5);
        queue.offer(1);
        System.out.println(queue.peek());
        int a= queue.poll();
        System.out.println(a);
        System.out.println(queue.peek());
        System.out.println(queue.size());

    }
}

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

BUUCTF_[安洵杯 2019]easy_web(preg_match绕过/MD5强碰撞绕过/代码审计)

打开靶场&#xff0c;出现下面的静态html页面&#xff0c;也没有找到什么有价值的信息。 查看页面源代码 在url里发现了img传参还有cmd 求img参数 这里先从img传参入手&#xff0c;这里我发现img传参好像是base64的样子 进行解码&#xff0c;解码之后还像是base64的样子再次进…

Linux的简单使用和部署4asszaaa0

一.部署 1 环境搭建方式主要有四种: 1. 直接安装在物理机上.但是Linux桌面使用起来非常不友好.所以不建议.[不推荐]. 2. 使用虚拟机软件,将Linux搭建在虚拟机上.但是由于当前的虚拟机软件(如VMWare之类的)存在⼀些bug,会导致环境上出现各种莫名其妙的问题比较折腾.[非常不推荐…

RK3566-移植5.10内核Ubuntu22.04

说明 记录了本人使用泰山派&#xff08;RK3566&#xff09;作为平台并且成功移植5.10.160版本kernel和ubuntu22.04&#xff0c;并且成功配置&连接网络的完整过程。 本文章所用ubuntu下载地址&#xff1a;ubuntu-cdimage-ubuntu-base-releases-22.04-release安装包下载_开源…

二级C语言题解:十进制转其他进制、非素数求和、重复数统计

目录 一、程序填空&#x1f4dd; --- 十进制转其他进制 题目&#x1f4c3; 分析&#x1f9d0; 二、程序修改&#x1f6e0;️ --- 非素数求和 题目&#x1f4c3; 分析&#x1f9d0; 三、程序设计&#x1f4bb; --- 重复数统计 题目&#x1f4c3; 分析&#x1f9d0; 前言…

UE求职Demo开发日志#22 显示人物信息,完善装备的穿脱

1 创建一个人物信息显示的面板&#xff0c;方便测试 简单弄一下&#xff1a; UpdateInfo函数&#xff1a; 就是获取ASC后用属性更新&#xff0c;就不细看了 2 实现思路 在操作目标为装备栏&#xff0c;或者操作起点为装备栏时&#xff0c;交换前先判断能否交换&#xff08;只…

在游戏本(6G显存)上本地部署Deepseek,运行一个14B大语言模型,并使用API访问

在游戏本6G显存上本地部署Deepseek&#xff0c;运行一个14B大语言模型&#xff0c;并使用API访问 环境说明环境准备下载lmstudio运行lmstudio 下载模型从huggingface.co下载模型 配置模型加载模型测试模型API启动API服务代码测试 deepseek在大语言模型上的进步确实不错&#xf…

专业学习|一文了解并实操自适应大邻域搜索(讲解代码)

一、自适应大邻域搜索概念介绍 自适应大邻域搜索&#xff08;Adaptive Large Neighborhood Search&#xff0c;ALNS&#xff09;是一种用于解决组合优化问题的元启发式算法。以下是关于它的详细介绍&#xff1a; -自适应大领域搜索的核心思想是&#xff1a;破坏解、修复解、动…

记录一下 在Mac下用pyinstallter 打包 Django项目

安装: pip install pyinstaller 在urls.py from SheepMasterOneToOne import settings from django.conf.urls.static import staticurlpatterns [path("admin/", admin.site.urls),path(generate_report/export/, ReportAdmin(models.Report, admin.site).generat…

如何在Intellij IDEA中识别一个文件夹下的多个Maven module?

目录 问题描述 理想情况 手动添加Module&#xff0c;配置Intellij IDEA的Project Structure 问题描述 一个文件夹下有多个Maven项目&#xff0c;一个一个开窗口打开可行但是太麻烦。直接open整个文件夹会发现Intellij IDEA默认可能就识别一个或者几个Maven项目&#xff0c;如…

Linux 文件和目录

Linux 文件和目录 文章目录 Linux 文件和目录Linux 目录Linux 目录配置的依据 --FHS目录树文件属性文件的分类一般权限 UGO特殊权限 suid\sgid\sticky隐藏属性 ATTR文件访问控制列表 ACL文件相关的命令权限的修改 chmod chown chgrp umaskchmodchgrpumask相关文档 /etc/profile…

【大数据技术】本机DataGrip远程连接虚拟机MySQL/Hive

本机DataGrip远程连接虚拟机MySQL/Hive datagrip-2024.3.4VMware Workstation Pro 16CentOS-Stream-10-latest-x86_64-dvd1.iso写在前面 本文主要介绍如何使用本机的DataGrip连接虚拟机的MySQL数据库和Hive数据库,提高编程效率。 安装DataGrip 请按照以下步骤安装DataGrip软…

【大模型】DeepSeek大模型技术路径

【大模型】DeepSeek大模型技术路径 一、总体架构(一)Transformer架构:奠定坚实基础(二)Mixture-of-Experts(MoE)架构:提升灵活性与效率二、技术突破(一)训练方法创新(二)架构优化(三)训练效率与成本优化(四)推理能力提升三、总结一、总体架构 DeepSeek大模型以…

【LLM-agent】(task2)用llama-index搭建AI Agent

note LlamaIndex 实现 Agent 需要导入 ReActAgent 和 Function Tool&#xff0c;循环执行&#xff1a;推理、行动、观察、优化推理、重复进行。可以在 arize_phoenix 中看到 agent 的具体提示词&#xff0c;工具被装换成了提示词ReActAgent 使得业务自动向代码转换成为可能&am…

解决Mac安装软件的“已损坏,无法打开。 您应该将它移到废纸篓”问题

mac安装软件时&#xff0c;如果出现这个问题&#xff0c;其实很简单 首先打开终端&#xff0c;输入下面的命令 sudo xattr -r -d com.apple.quarantine 输入完成后&#xff0c;先不要回车&#xff0c;点击访达--应用程序--找到你无法打开的app图标&#xff0c;拖到终端窗口中…

(9) 上:学习与验证 linux 里的 epoll 对象里的 EPOLLIN、 EPOLLHUP 与 EPOLLRDHUP 的不同

&#xff08;1&#xff09;经过之前的学习。俺认为结论是这样的&#xff0c;因为三次握手到四次挥手&#xff0c;到 RST 报文&#xff0c;都是 tcp 连接上收到了报文&#xff0c;这都属于读事件。所以&#xff1a; EPOLLIN : 包含了读事件&#xff0c; FIN 报文的正常四次挥手、…

一文讲解Spring如何解决循环依赖

Spring 通过三级缓存机制来解决循环依赖&#xff1a; 一级缓存&#xff1a;存放完全初始化好的单例 Bean。 二级缓存&#xff1a;存放正在创建但未完全初始化的 Bean 实例。 三级缓存&#xff1a;存放 Bean 工厂对象&#xff0c;用于提前暴露 Bean。 试问:三级缓存解决循环依…

Vue canvas画图画线例子,数据回显与隔离,点拖拽修改

组件 <template><divstyle"display: flex; height: 342px; width: 760px; border: 1px solid #000"><divstyle"position: relative; height: 100%; width: 608px; min-width: 608px"><canvasid"mycanvas"ref"mycanva…

【自动化办公】批量图片PDF自定义指定多个区域识别重命名,批量识别铁路货物运单区域内容改名,基于WPF和飞桨ocr深度学习模型的解决方案

项目背景介绍 铁路货运企业需要对物流单进行长期存档&#xff0c;以便后续查询和审计。不同的物流单可能包含不同的关键信息&#xff0c;通过自定义指定多个区域进行识别重命名&#xff0c;可以使存档的图片文件名具有统一的规范和明确的含义。比如&#xff0c;将包含货物运单…

洛谷网站: P3029 [USACO11NOV] Cow Lineup S 题解

题目传送门&#xff1a; P3029 [USACO11NOV] Cow Lineup S - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 前言&#xff1a; 这道题的核心问题是在一条直线上分布着不同品种的牛&#xff0c;要找出一个连续区间&#xff0c;使得这个区间内包含所有不同品种的牛&#xff0c;…

K8S Deployment 实现 蓝绿 发布

一、何为蓝绿发布 蓝绿发布&#xff08;Blue - Green Deployment&#xff09;是一种软件部署策略&#xff0c;旨在最大程度减少应用程序停机时间&#xff0c;确保新老版本系统平稳过渡。以下为详细介绍&#xff1a; 1.1、基本概念 存在两个完全相同的生产环境&#xff0c;通…