数据结构-堆和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;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic 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());}
}

 

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

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

相关文章

oracle 基础语法复习记录

Oracle SQL基础 因工作需要sql能力&#xff0c;需要重新把sql这块知识重新盘活&#xff0c;特此记录学习过程。 希望有新的发现。加油&#xff01;20250205 学习范围 学习SQL基础语法 掌握SELECT、INSERT、UPDATE、DELETE等基本操作。 熟悉WHERE、GROUP BY、ORDER BY、HAVIN…

【Rust自学】20.2. 最后的项目:多线程Web服务器

说句题外话&#xff0c;这篇文章非常要求Rust的各方面知识&#xff0c;最好看一下我的【Rust自学】专栏的所有内容。这篇文章也是整个专栏最长&#xff08;4762字&#xff09;的文章&#xff0c;需要多次阅读消化&#xff0c;最好点个收藏&#xff0c;免得刷不到了。 喜欢的话…

国产编辑器EverEdit - 工具栏说明

1 工具栏 1.1 应用场景 当用户想显示/隐藏界面的标签栏、工具栏、状态栏、主菜单等界面元素时&#xff0c;可以通过EverEdit的菜单选项进行设置。 1.2 使用方法 选择菜单查看 -> 工具栏&#xff0c;在工具栏的子菜单中选择勾选或去掉勾选对应的选项。 标签栏&#xff1…

虚幻UE5手机安卓Android Studio开发设置2025

一、下载Android Studio历史版本 步骤1&#xff1a;虚幻4.27、5.0、5.1、5.2官方要求Andrd Studio 4.0版本&#xff1b; 5.3、5.4、5.5官方要求的版本为Android Studio Flamingo | 2022.2.1 Patch 2 May 24, 2023 虚幻官网查看对应Andrd Studiob下载版本&#xff1a; https:/…

VLAN 基础 | 不同 VLAN 间通信实验

注&#xff1a;本文为 “ Vlan 间通信” 相关文章合辑。 英文引文&#xff0c;机翻未校。 图片清晰度限于原文图源状态。 未整理去重。 How to Establish Communications between VLANs? 如何在 VLAN 之间建立通信&#xff1f; Posted on November 20, 2015 by RouterSwi…

bat脚本实现自动化漏洞挖掘

bat脚本 BAT脚本是一种批处理文件&#xff0c;可以在Windows操作系统中自动执行一系列命令。它们可以简化许多日常任务&#xff0c;如文件操作、系统配置等。 bat脚本执行命令 echo off#下面写要执行的命令 httpx 自动存活探测 echo off httpx.exe -l url.txt -o 0.txt nuc…

堆的实现——堆的应用(堆排序)

文章目录 1.堆的实现2.堆的应用--堆排序 大家在学堆的时候&#xff0c;需要有二叉树的基础知识&#xff0c;大家可以看我的二叉树文章&#xff1a;二叉树 1.堆的实现 如果有⼀个关键码的集合 K {k0 , k1 , k2 , …&#xff0c;kn−1 } &#xff0c;把它的所有元素按完全⼆叉树…

edu小程序挖掘严重支付逻辑漏洞

edu小程序挖掘严重支付逻辑漏洞 一、敏感信息泄露 打开购电小程序 这里需要输入姓名和学号&#xff0c;直接搜索引擎搜索即可得到&#xff0c;这就不用多说了&#xff0c;但是这里的手机号可以任意输入&#xff0c;只要用户没有绑定手机号这里我们输入自己的手机号抓包直接进…

FRP通过公网IP实现内网穿透

FRP通过公网IP实现内网穿透 一、简介二、安装服务端1、下载2、安装FRP3、使用 systemd 命令管理 frps 服务4、设置 frps 开机自启动 三、安装客户端1、下载2、安装FRP3、使用 systemd 命令管理 frpc 服务4、设置 frpc 开机自启动 四、访问仪表盘 一、简介 frp 是一款高性能的反…

K8S学习笔记-------1.安装部署K8S集群环境

1.修改为root权限 #sudo su 2.修改主机名 #hostnamectl set-hostname k8s-master01 3.查看网络地址 sudo nano /etc/netplan/01-netcfg.yaml4.使网络配置修改生效 sudo netplan apply5.修改UUID&#xff08;某些虚拟机系统&#xff0c;需要设置才能生成UUID&#xff09;#…

go运算符

内置运算符 算术运算符关系运算符逻辑运算符位运算符赋值运算符 算术运算符 注意&#xff1a; &#xff08;自增&#xff09;和–&#xff08;自减&#xff09;在 Go 语言中是单独的语句&#xff0c;并不是运算符 package mainimport "fmt"func main() {fmt.Printl…

【贪心算法篇】:“贪心”之旅--算法练习题中的智慧与策略(一)

✨感谢您阅读本篇文章&#xff0c;文章内容是个人学习笔记的整理&#xff0c;如果哪里有误的话还请您指正噢✨ ✨ 个人主页&#xff1a;余辉zmh–CSDN博客 ✨ 文章所属专栏&#xff1a;贪心算法篇–CSDN博客 文章目录 一.贪心算法1.什么是贪心算法2.贪心算法的特点 二.例题1.柠…

一款wordpress AI免费插件自动内容生成+前端AI交互+文章批量采集

一款wordpressAI自动内容生成前端AI会话窗口交互文章批量采集免费插件 1. SEO优化文章生成 关键词驱动的内容生成&#xff1a;用户可以输入关键词或长尾关键词&#xff0c;插件会根据这些关键词生成高质量的SEO优化文章。文章结构清晰&#xff0c;语言自然流畅&#xff0c;符合…

Linux03——常见的操作命令

root用户以及权限 Linux系统的超级管理员用户是&#xff1a;root用户 su命令 可以切换用户&#xff0c;语法&#xff1a;su [-] [用户名]- 表示切换后加载环境变量&#xff0c;建议带上用户可以省略&#xff0c;省略默认切换到root su命令是用于账户切换的系统命令&#xff…

使用 Ollama 在 Windows 环境部署 DeepSeek 大模型实战指南

文章目录 前言Ollama核心特性 实战步骤安装 Ollama验证安装结果部署 DeepSeek 模型拉取模型启动模型 交互体验命令行对话调用 REST API 总结个人简介 前言 近年来&#xff0c;大语言模型&#xff08;LLM&#xff09;的应用逐渐成为技术热点&#xff0c;而 DeepSeek 作为国产开…

关于大数据

在大数据背景下存在的问题&#xff1a; 非结构化、半结构化数据&#xff1a;NoSQL数据库只负责存储&#xff1b;程序处理时涉及到数据移动&#xff0c;速度慢 是否存在一套整体解决方案&#xff1f; 可以存储并处理海量结构化、半结构化、非结构化数据 处理海量数据的速…

通过docker安装部署deepseek以及python实现

前提条件 Docker 安装:确保你的系统已经安装并正确配置了 Docker。可以通过运行 docker --version 来验证 Docker 是否安装成功。 网络环境:保证设备有稳定的网络连接,以便拉取 Docker 镜像和模型文件。 步骤一:拉取 Ollama Docker 镜像 Ollama 可以帮助我们更方便地管理…

企业四要素如何用PHP进行调用

一、什么是企业四要素&#xff1f; 企业四要素接口是在企业三要素&#xff08;企业名称、统一社会信用代码、法定代表人姓名&#xff09;的基础上&#xff0c;增加了一个关键要素&#xff0c;通常是企业注册号或企业银行账户信息。这种接口主要用于更全面的企业信息验证&#x…

Android性能优化系列——卡顿优化

卡顿&#xff0c;就是用户体感界面不流畅。我们知道手机的屏幕画面是按照一定频率来刷新的&#xff0c;理论上讲&#xff0c;24 帧的画面更新就能让人眼感觉是连贯的。但是实际上&#xff0c;这个只是针对普通的视频而言。对于一些强交互或者较为敏感的场景来说&#xff0c;比如…

激光工控机在自动化领域中有哪些作用?

首先是对于高精度加工控制方面&#xff1a;激光工控机能够精确控制激光光束的运动轨迹和输出功率&#xff0c;实现对各种材料的精细切割、雕刻和焊接&#xff0c;保证加工质量和效率。 其次还能实时监控与远程控制激光工控机凭借其强大的网络通信功能&#xff0c;可以实时监控…