【Java】还在死磕算法?懂“堆”与“优先级队列”,代码效率飙升

【Java】还在死磕算法?懂“堆”与“优先级队列”,代码效率飙升
dc49951ad219454bbef81e408b809a6b.gif

个人主页:喜欢做梦

欢迎 💛点赞  🌟收藏 💫关注


🏆堆

一、🎯什么是堆

堆的概念

堆是一种特殊的完全二叉树,如果有一个关键码的集合K={k0,k1,k2,...,kn-1},把它所有的元素按照完全二叉树的顺序存储方式一维数组中,并满足:Ki<=K2i+1且Ki<=K2i+2(Ki>=K2i+2)i=0,1,2,3....,则称为小堆。堆有两种类型分别为大根堆小根堆:小根堆:根节点的值最小,父节点的值小于或等于其孩子节点的值;大根堆:根节点的值最大,父节点的值大于或等于其孩子节点的值;

堆的性质

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

二、🀄️堆的创建

大堆

实现过程:

255b7fc12bd94eed89801a9e8ceccba9.png
ec55d38b93bd4e43a40ff20226edf8c7.png

代码: 

public class Heap { public int[] elem;//数组 public int usedSize;//使用大小 public Heap() { this.elem=new int[10]; } //初始化 public void initHeap(int[] elem){ for (int i = 0; i <elem.length ; i++) { //初始化 this.elem[i]=elem[i]; //使用大小增加 usedSize++; } } //建立大堆将父节点向下调整 //思路:从从后向上将父节点向下调整; //父节点表示:(usedSize-1-1)/2; //左孩子节点表示为2*parent+1 //1.找出左右孩子节点中的哪个元素最大 //2.将父节点与左右孩子节点的最大值进行比较; // 如果小于,将元素进行交换;随后将父节点跳到孩子节点的位置,直到最后一个节点 // 如果大于,退出该次循环,进入到下一次循环 public void creatHeap(){ for (int parent = (usedSize-1-1)/2; parent >=0; parent--) { //parent:调整的起始位置; //usedSize:调整的结束位置 siftDown(parent,usedSize); } } public void siftDown(int parent,int usedSize){ int child=2*parent+1; while(child<usedSize){ //比较左右节点 //如果左节点小于右节点 //将孩子节点++,到右节点的位置 if(child+1<usedSize && elem[child]<elem[child+1]){ child++; } //比较父节点与子节点 //如果小于子节点,交换两元素位置 if(elem[child]>elem[parent]){ swap(elem,child,parent); //交换完,将父节点等于下一个子节点,看下一个堆是否形成大堆, //如果没有,继续交换 parent=child; child=2*parent+1; }else{ //如果大于子节点,不调整 break; } } } private void swap(int[] elem,int i,int j){ int tmp=elem[i]; elem[i]=elem[j]; elem[j]=tmp; } }

小堆

小堆的思路与大堆相同,只不过一个父节点比子孩子小

代码:

 private void swap(int[] elem,int i,int j){ int tmp=elem[i]; elem[i]=elem[j]; elem[j]=tmp; } public void creatHeap2(){ for (int parent = (usedSize-1-1)/2; parent >=0; parent--) { siftDown2(parent,usedSize); } } public void siftDown2(int parent,int usedSize){ int child=2*parent+1; while(child<usedSize){ if(child+1<usedSize && elem[child]>elem[child+1]){ child++; } if(elem[child]<elem[parent]){ swap(elem,child,parent); parent=child; child=2*parent+1; }else{ break; } } }
  • 建堆的时间复杂度为O(n); 

测试:

 public static void main(String[] args) { int[] array={15,28,17,36,45,23,35,56}; Heap heap=new Heap(); heap.initHeap(array); heap.creatHeap1(); for (int i = 0; i < heap.elem.length; i++) { System.out.print(heap.elem[i]+" "); } System.out.println(); heap.creatHeap2(); for (int i = 0; i < heap.elem.length; i++) { System.out.print(heap.elem[i]+" "); } }

三、🎨堆的基本操作

堆的插入

思路:看位置是否已满,如果满了扩容;插入元素:将元素插入在最后一个节点后面,也就是插入在elem[uesdSize];随后将其进行向上调整;          
  • 向上调整:将子节点进行向上调整

向上调整的实现过程:

f9f637c09bdf49df9653efdc74b7d5eb.png
d9c4fadd30d64c8398a3e482eb352ecd.png

实现代码:

 public void offer(int val){ if(isFull()){ this.elem= Arrays.copyOf(elem,2*elem.length); } elem[usedSize]=val; siftUp(usedSize); usedSize++; } private boolean isFull(){ return this.usedSize== elem.length; } //向上调整: //将子节点与父节点进行比较; //如果大于父节点,交换;随后减子节点跳到父节点的位置,父节点跳到父节点的位置 //小于退出本次循环 public void siftUp(int child){ int parent=(child-1)/2; //循环条件:parent>=0 while(parent>=0){ if(elem[parent]<elem[child]){ swap(elem,parent,child); child=parent; parent=(child-1)/2; }else{ break; } } }}

堆的删除

堆的删除:删除堆顶元素

思路将堆顶元素与最后一个节点元素进行交换,也就是位置0的元素与位置usedSize的元素进行交换交换后,只有0位置开始不是大堆,所以我们从0开始进行向下调整;调整完将使用大小减小

调整过程:

0a74d1887a4c42619750f0ef10db266c.png
7729a17b0199400a9d02b1db452f3284.png

 代码:

 public int poll(){ if(isEmpty()){ return -1; } int val=elem[0]; swap(elem,0,usedSize-1); siftDown1(0,usedSize-1); usedSize--; return val; } private boolean isEmpty(){ return this.usedSize==0; }

获取堆顶元素

//获取堆顶元素 public int peek(){ if (isEmpty()){ return -1; } return elem[0]; }

 堆的排序(从小到大)

思路:将最后一个元素与第一个元素进行交换,也就是将最大值放在最后一个;随后将交换在第一个位置的元素进行向下调整到交换到最后的那些数之前;调整完,也就是倒数第二大的元素在堆顶,将其与倒二个元素进行交换,交换完调整,其他同样如此;

调整过程:

33ec44c2c1ca40b29375a7531966658d.png
e2bb37a7d11b485480655f8df0c1bd66.png
26c51c9a2d284e8f9b7ae6a56d5d5313.png
62eb16a0edd14dd28b0f15fcb2108668.png
 public void heapSort(){ int end=usedSize-1; while(end>0) { swap(elem, 0, end); siftDown1(0,end); end--; }

向下调整和向上调整的区别:

区别向下调整向上调整
方向父节点向下调整子节点向上调整
比较对象主要父节点与子节点最值进行比较主要新插入的子节点与父节点进行比较
触发场景删除或更新操作插入操作

🏆优先级队列(Priority Queue)

一、📖优先级队列的概念

优先级队列:

是队列的一种,遵循先进先出的原则。但是其元素有优先级之分,在出队时,优先出队,优先级最高的元素。比如在医院急诊时,优先治疗病情严重的病人。 
  • 优先级队列底层使用了堆这种数据结构。 

注意事项:

  • 使用时必须导入PriorityQueue所在的包;
  •  PriorityQueue中放置的元素必须能够比较大小,否则抛出异常;
  • 不能插入null对象,否则抛出异常;
  • 没有容量限制,可以任意插入多个元素,自动扩容;
如果容量小于64进行2倍扩容;如果容量大于等于64进行1.5倍扩容;如果容量超过Max_ARRAY_Size,按照Max_ARRAY_Size来进行扩容;
  • PriorityQueue底层使用了堆数据结构;
  • PriorityQueue默认情况下是小堆;

插入和删除的时间复杂度为O(

eq?%5Clog_%7B2%7D

N);

 如果要PriorityQueue变为大堆,可以重写compare方法:

二、⛳️优先级队列的使用

PriorityQueue的构造方法:

187713b95cdd47e1996147db87086729.png

PriorityQueue的方法

516d8fd4a85343b18d7c99393ab65def.png
 public static void main(String[] args) { PriorityQueue<Integer> priorityQueue=new PriorityQueue<>(); //插入元素 priorityQueue.offer(24); priorityQueue.offer(12); priorityQueue.offer(35); priorityQueue.offer(20); //获取优先级最高的元素 priorityQueue.peek(); //删除指定元素 priorityQueue.remove(35); //删除优先级最高的元素 priorityQueue.poll(); //..... }

 

07237e5031ba47fb98dc5e8b90759947.jpeg

感谢支持!!! 🌹 🌹 🌹

Read more

基于 Python 与 GitHub,打造个人专属本地化思维导图工具全流程方案(上)

基于 Python 与 GitHub,打造个人专属本地化思维导图工具全流程方案(上)

基于 Python 与 GitHub,打造个人专属本地化思维导图工具全流程方案(上) 各位博友,自从踏入修真界,就整天想怎样把代码改造成绝世技能。这不又有新思路,准备用 Python 和 GitHub 这两把 “趁手仙器”,从零开始打造一个专属于自己的本地化思维导图工具。 这工具啥特色?轻量到能揣兜里跑(内存占用低),颜值随你心意改(界面可自定义),还能离线玩得转(数据全存本地)。不管你是想理清楚小说剧情线、课堂笔记,还是规划个小项目,它都能支棱起来。咱不整那些花里胡哨的框架套路,就靠最基础的 HTML/CSS/JS 和 Python,一步步带你打通 “开发任督二脉”:从拆解开源项目优点,到写代码时的 “挖坑填坑”,再到最后打包成能双击运行的 EXE 文件,每一步都给你掰扯得明明白白。 放心,就算你是刚摸到键盘的 “练气期” 萌新,跟着咱的节奏走,也能亲手造出趁手的

By Ne0inhk
BeyondCompare安装(永久免费使用+全网最详细版)

BeyondCompare安装(永久免费使用+全网最详细版)

一.下载: * 阿里云盘(不限速) https://www.alipan.com/s/WaG1z54BQ2U 官网下载(速度较慢): https://www.scootersoftware.com/download.php 二.安装(无脑下一步即可) 三.永久免费使用: 1. 在搜索栏中输入 regedit ,打开注册表 2. 删除项目:计算机 \HKEY_CURRENT_USER\Software\ScooterSoftware\Beyond Compare 4\CacheId 修改注册表 四.每周自动删掉CacheId: 1.创建删除CacheId脚本,命名为freshBeyondcompare4.bat(注意:这里不要放在有中文路径的文件夹下) ```python # 内容如下:

By Ne0inhk
【AI大模型前沿】通义万相Wan2.2:阿里270亿参数巨兽开源,消费级显卡就能跑,免费平替Sora上线

【AI大模型前沿】通义万相Wan2.2:阿里270亿参数巨兽开源,消费级显卡就能跑,免费平替Sora上线

系列篇章💥 No.文章1【AI大模型前沿】深度剖析瑞智病理大模型 RuiPath:如何革新癌症病理诊断技术2【AI大模型前沿】清华大学 CLAMP-3:多模态技术引领音乐检索新潮流3【AI大模型前沿】浙大携手阿里推出HealthGPT:医学视觉语言大模型助力智能医疗新突破4【AI大模型前沿】阿里 QwQ-32B:320 亿参数推理大模型,性能比肩 DeepSeek-R1,免费开源5【AI大模型前沿】TRELLIS:微软、清华、中科大联合推出的高质量3D生成模型6【AI大模型前沿】Migician:清华、北大、华科联手打造的多图像定位大模型,一键解决安防监控与自动驾驶难题7【AI大模型前沿】DeepSeek-V3-0324:AI 模型的全面升级与技术突破8【AI大模型前沿】BioMedGPT-R1:清华联合水木分子打造的多模态生物医药大模型,开启智能研发新纪元9【AI大模型前沿】DiffRhythm:西北工业大学打造的10秒铸就完整歌曲的AI歌曲生成模型10【AI大模型前沿】R1-Omni:阿里开源全模态情感识别与强化学习的创新结合11【AI大模型前沿】Qwen2.5-Omni:

By Ne0inhk

【研发规范】Git 提交(commit)、CodeReview规范

本文将分为三个部分: 1. 为什么需要提交规范? 2. 提交规范详解(核心内容) 3. 与 Code Review 流程的结合 1. 为什么需要提交规范? 在 Code Review 前,如果提交的代码杂乱无章,审查者会非常痛苦: * 理解成本高:审查者需要花费大量时间猜测这个提交到底做了什么和为什么这么做。 * 范围不明确:一个提交里混杂了多个功能的修改,难以聚焦审查。 * 历史追溯困难:混乱的提交信息使得日后排查问题、生成变更日志(Changelog)变得几乎不可能。 良好的提交规范旨在解决这些问题,它的核心目标是:让每一次提交都是一个逻辑独立、意图明确、易于理解的故事单元。 2. 提交规范详解 一份优秀的提交(Commit)主要由两部分组成: 1. 提交信息 2. 提交内容(代码变更集) A. 提交信息规范 提交信息是写给未来维护者(包括你自己) 的说明文档。一个常见的规范格式是:

By Ne0inhk