一、堆的概念

堆(Heap)是一种特殊的基于树的数据结构,通常分为最大堆和最小堆两种类型。它满足堆属性:对于最大堆,父节点的值总是大于或等于其子节点的值;而对于最小堆,父节点的值总是小于或等于其子节点的值。

数组实现:虽然堆是一个基于树的结构,但它通常用数组来实现。对于位于数组索引i处的元素,其左孩子的索引是2*i + 1,右孩子的索引是2*i + 2,而其父节点的索引是(i-1)/2

将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆的性质: 堆中某个节点(孩子节点)的值总是不大于或不小于其父节点的值;

堆总是一棵完全二叉树

2.堆得存储方式

二叉树层序遍历存储在数组中

1)堆的创建(大根堆)

向下调整法 (同理可创建小根堆,孩子中最小的与根节点交换)

 完全二叉树从最后一棵子树慢慢调整为堆,最后一课子树根节点与左右孩子最大值交换(根节点<左/右孩子).然后记得递归调整堆结构被破坏的子树

package org.example;import java.util.Arrays;public class TestHeap {private int[] elem;private int usedsize;public  TestHeap(int capacity){this.elem=new int[capacity];}//初始化堆public  void initHeap(int []array){for (int i = 0; i < array.length ; i++) {elem[i]=array[i];usedsize++;//数组堆元素个数}}public void display(){System.out.println(Arrays.toString(elem));}//创建堆//一棵子树有孩子一定先有左孩子 左孩子(usedsize-1-1)/2->父亲节点public void createHeap(){//从最后一棵子树根节点按个遍历每棵树根节点for(int parent=((usedsize-1-1)/2);parent>=0;parent--){shiftDown(parent,usedsize);}}public void shiftDown(int parent,int usedsize){int child=(2*parent)+1;//左孩子节点下标while(child<usedsize){//要确保有右孩子且不越界if(child+1<usedsize&&elem[child]<elem[child+1]){//右孩子比左孩子大 找到孩子节点的最大值child++;}//child是左右孩子节点最大值的下标if(elem[child]>elem[parent]){//交换节点swap(child,parent);//父节点向下移,继续调整parent=child;child=2*parent+1;}else {//已经是大根堆不需要调整break;}}}public void swap(int i,int j){int tmp=elem[i];elem[i]=elem[j];elem[j]=tmp;}}

Test测试类

package org.example;import java.util.Arrays;public class Test {public static void main(String[] args) {int []array={27,15,19,18,28,34,65,49,25,37};TestHeap testheap=new TestHeap(array.length);testheap.initHeap(array);testheap.createHeap();testheap.display();}
}

 

创建前

创建后(大根堆)

 2)堆的插入

向上调整(先将新插入的节点放到堆的末尾,最后一个孩子之后,堆的结构性质被破坏,在慢慢往上移,向上调整为大/小根堆)

注意:空间放满时需要扩容

 public void offer(int val){if(isfull()){this.elem=Arrays.copyOf(elem,2*elem.length);//向上调整}//添加的元素是新的孩子this.elem[usedsize]=val;shiftUp(usedsize);usedsize++;}//向上调整函数 (也创建堆)//向下调整方法比向上调整方法少遍历(最后)一层节点public void  shiftUp(int child){int parent=(child-1)/2;while (child>0){if(elem[child]>elem[parent]){swap(child,parent);child=parent;parent=(child-1)/2;}else {break;}}}public  boolean isfull(){return elem.length==usedsize;}

main方法

int []array={27,15,19,18,28,34,65,49,25,37};TestHeap testheap=new TestHeap(array.length);testheap.initHeap(array);testheap.createHeap();//testheap.display();testheap.offer(88);//插入元素打印testheap.display();

3)堆的删除

删除的一定是堆顶(根节点元素)

a.将堆顶元素与最后一个孩子元素交换

b.将堆中有效元素的个数减少一个

c.将堆顶元素向下调整,满足堆特性为止

    public int pool(){//一定是删除堆顶元素int tmp= elem[0];//保留删除的元素//交换元素swap(0,usedsize-1);usedsize--;shiftDown(0,usedsize);//返回被删除的元素return tmp;}
 int val=testheap.pool();System.out.println("被删除的元素是"+val);//删除后元素打印testheap.display();

    public void display(){// System.out.println(Arrays.toString(elem));//显示堆的有效部分System.out.println(Arrays.toString(Arrays.copyOf(elem, usedsize)));}

打印方法display()修改原因

你遇到的问题是:

> 删除堆顶元素(65)后,65 **仍然出现在数组中**,只是被“挤”到了最后面,数组并没有真正缩小,65 依然存在。

---

## ✅ 你的代码是**逻辑正确的**,但你对 `display()` 方法的理解有误。

---

## 🧠 堆的底层实现是一个数组,但它**不是动态收缩的数组**,而是通过 `usedsize` 来控制堆的有效元素数量。

你调用 `pool()` 方法后:

```java
usedsize--;
```

这意味着堆的有效长度减少了 1,但底层数组并没有变小,**只是堆不再“关心”最后一个元素**。

---

## 🔍 举个例子

你原来的堆是:

```
[65, 49, 34, 25, 37, 27, 19, 18, 15, 28]
```

调用 `pool()` 后,堆会执行:

```java
swap(0, usedsize - 1); // 交换 65 和 28
usedsize--;            // usedsize 从 10 变成 9
shiftDown(0, usedsize); // 从新的堆顶 28 开始下滤
```

此时数组变成:

```
[49, 37, 34, 25, 28, 27, 19, 18, 15, 65]
```

但是 `usedsize = 9`,意味着堆只“看到”前 9 个元素:

```
[49, 37, 34, 25, 28, 27, 19, 18, 15]
```

而 `65` 被留在数组最后,**只是不再属于堆的范围**。

---

## ✅ 所以你看到的输出:

```
[49, 37, 34, 25, 28, 27, 19, 18, 15, 65]
```

这是完全正常的,因为:

- `display()` 方法打印的是整个数组(包括不属于堆的部分)
- 你没有重新拷贝数组或清空无效数据

---

## ✅ 如何正确打印堆的有效部分?

修改 `display()` 方法为只打印有效部分:

```java
public void display() {
System.out.println(Arrays.toString(Arrays.copyOf(elem, usedsize)));
}
```

## ✅ 修改后输出:

调用 `testheap.display();` 会输出:

```
49 37 34 25 28 27 19 18 15
```

二、优先级队列

队列是一种先进先出(FIFO)的数据结构,但有些情况下,操作的数据可能带有优先级,一般出队 列时,可能需要优先级高的元素先出队列,该中场景下,使用队列显然不合适,比如:在手机上玩游戏的时候,如 果有来电,那么系统应该优先处理打进来的电话;

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

JDK1.8中的PriorityQueue底层使用了堆这种数据结构,而堆实际就是在完全二叉树的基础上进行了一些调整。

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

1)优先级队列使用

导包

import java.util.PriorityQueue;

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

3. 不能插入null对象,否则会抛出NullPointerException

4. 没有容量限制,可以插入任意多个元素,其内部可以自动扩容

5. 插入和删除元素的时间复杂度为

6. PriorityQueue底层使用了堆数据结构

7. PriorityQueue默认情况下是小堆---即每次获取到的元素都是最小的元素

public class Test {public static void main(String[] args) {PriorityQueue<Integer> priorityQueue=new PriorityQueue<>();priorityQueue.offer(10);priorityQueue.offer(4);priorityQueue.offer(8);priorityQueue.offer(3);priorityQueue.offer(7);System.out.println(priorityQueue.poll());System.out.println(priorityQueue.poll());System.out.println(priorityQueue.poll());System.out.println(priorityQueue.poll());System.out.println(priorityQueue.poll());}

打印结果证明是小根堆

 2)优先级队列的创建方式(3种)

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());
}

3)优先级队列的使用方法

函数名                                  功能介绍

boolean offer(E e)    插入元素e,插入成功返回true,如果e对象为空,抛出NullPointerException异常,时间复杂度 log2N,注意:空间不够时候会进行扩容

E peek() 获取优先级最高的元素,如果优先级队列为空,返回null

E poll() 移除优先级最高的元素并返回,如果优先级队列为空,返回null

int size() 获取有效元素的个数

void clear() 清空

boolean isEmpty() 检测优先级队列是否为空,空返回true

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

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

相关文章

踩坑记录:因版本不匹配导致 Boost 1.85 编译失败的完整解决过程

踩坑记录&#xff1a;因版本不匹配导致 Boost 1.85 编译失败的完整解决过程 转载请注明出处&#xff0c;欢迎评论区交流。 背景 最近在 Windows 11 VS2022 环境下尝试用 b2 编译 Boost 1.85.0&#xff0c;结果一路踩坑&#xff0c;最后发现罪魁祸首是 Boost.Build 自带的 msv…

InfluxDB Line Protocol 协议深度剖析(二)

四、Line Protocol 写入操作与实践&#xff08;一&#xff09;HTTP API 写入使用 HTTP API 是通过 Line Protocol 写入数据到 InfluxDB 的常用方式。InfluxDB 1.x&#xff1a;请求方式为 POST&#xff0c;URL 格式为 “http://host:port/write?dbdatabase_name”。其中&#x…

负载均衡:提升业务性能的关键技术

一.负载均衡&#xff08;3.6 &#xff09;1.1.什么是负载均衡&#xff1a;负载均衡&#xff1a;Load Balance&#xff0c;简称LB&#xff0c;是一种服务或基于硬件设备等实现的高可用反向代理技术&#xff0c;负载均 衡将特定的业务(web服务、网络流量等)分担给指定的一个或多个…

【STM32项目】智能家居(版本1)

✌️✌️大家好&#xff0c;这里是5132单片机毕设设计项目分享&#xff0c;今天给大家分享的是基于《基于STM32的智能家居设计》。 目录 1、系统功能 2.1、硬件清单 2.2、功能介绍 2.3、控制模式 2、演示视频和实物 3、系统设计框图 4、软件设计流程图 5、原理图 6、主…

OpenSCA开源社区每日安全漏洞及投毒情报资讯—2025年7月24日

2025年7月24日安全风险情报资讯在野漏洞风险&#xff08;CVE未收录&#xff09;&#xff1a;1公开漏洞精选&#xff1a;2组件投毒情报&#xff1a;2在野漏洞风险&#xff08;CVE未收录&#xff09;1.1 gemini-cli项目潜在命令注入漏洞项目详情项目描述&#xff1a;gemini-cli是…

飞算 JavaAI 深度实战:从老项目重构到全栈开发的降本增效密码

飞算 JavaAI 深度实战&#xff1a;从老项目重构到全栈开发的降本增效密码引言正文一、智能引导模块&#xff1a;老项目重构的 “手术刀” 级解决方案1.1 本地化智能分析&#xff1a;IDEA 插件实操演示1.1.1 &#x1f4cc; IDEA 插件安装步骤1.1.1.1 首先打开idea工具&#xff0…

分布式推客系统开发全解:微服务拆分、佣金结算与风控设计

一、推客系统概述与市场背景推客系统&#xff08;也称为分销系统或社交电商系统&#xff09;已成为现代电商平台和内容平台的重要增长引擎。根据最新统计数据&#xff0c;2023年社交电商市场规模已突破3万亿元&#xff0c;占整体电商市场份额的25%以上。推客系统的核心价值在于…

Linux tcpdump 抓取udp 报文

一、tcpdump 支持命令选项tcpdump -i # 指定监听网络接口tcpdump -w # 将捕获到的信息保存到文件中&#xff0c;且不分析和打印在屏幕tcpdump -r # 从文件中读取数据tcpdump -n # 不把 ip 转化成域名tcpdump -t # 在每行的输出中不显示时间tcpdump -v # 产生详细的输出tc…

Oracle数据块8KB、OS默认认块管理4KB,是否需调整大小为一致?

上班路上&#xff0c;脑中忽然闪现一个问题&#xff1a;Oracle数据库块大小&#xff08;8KB&#xff09;、操作系统文件系统块大小&#xff08;4KB&#xff09;&#xff0c;为了减少IOPS&#xff0c;需不需要调整为一致&#xff1f;在数据块保持一致的情况下&#xff0c;针对频…

卡尔曼滤波器噪声方差设置对性能影响的仿真研究

卡尔曼滤波器噪声方差设置对性能影响的仿真研究 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家,觉得好请收藏。点击跳转到网站。 1. 引言 卡尔曼滤波器是一种广泛应用于信号处理、控制系统、导航系统等领域的递归估计算法。它通过对系…

“多线程修路:当count++变成灾难现场”

1.现象 当我们操作一个线程池的时候&#xff0c;可能需要去计数&#xff0c;也就是统计count&#xff0c;那我们这里有一个疑问&#xff0c;会不会产生线程安全问题&#xff1f; 毫无疑问绝对会有线程安全问题。在线程池环境中&#xff0c;多个线程并发访问和修改一个共享的 co…

GaussDB null的用法

1 null的定义null 空值代表丢失的未知数据。 默认情况下&#xff0c;表列可以保存 null 值。 本章解释 is null 和 is not null 操作符。2 null值的赘述如果表中的列是可选的&#xff0c;那么我们可以插入一个新记录或更新一个现有记录&#xff0c;而无 需向列添加一个值。这意…

智慧农业新图景:物联网如何精准守护作物生长​

在传统农业生产模式下&#xff0c;农民往往凭借经验判断作物生长需求&#xff0c;灌溉、施肥缺乏精准性&#xff0c;导致水资源浪费、土壤板结、作物产量与品质难以提升等问题。加之气候变化无常&#xff0c;极端天气频发&#xff0c;给农业生产带来诸多不确定性&#xff0c;传…

[ComfyUI] -入门2- 小白零基础搭建ComfyUI图像生成环境教程

AI图像生成已经成为AIGC(人工智能生成内容)领域的重要组成部分,而ComfyUI作为一款可视化的Stable Diffusion工作流工具,以其模块化、高度自由化的特点吸引了越来越多创作者的关注。本文将手把手教你如何在Windows系统下,从零搭建属于自己的ComfyUI图像生成环境。 一、Comf…

java设计模式 -【单例模式】

单例模式的定义 单例模式&#xff08;Singleton Pattern&#xff09;是一种创建型设计模式&#xff0c;确保一个类只有一个实例&#xff0c;并提供一个全局访问点。常用于需要控制资源或共享状态的场景&#xff0c;例如数据库连接、日志记录器等 单例模式的实现方式 饿汉式&…

Flink 自定义类加载器和子优先类加载策略

子类优先加载Flink 默认采用了子优先&#xff08;Child-First&#xff09;的类加载策略来加载用户代码&#xff0c;以解决潜在的依赖冲突问题。我们可以通过源码来证明这一点。ChildFirstClassLoader 的实现Flink 中负责实现“子优先”加载逻辑的核心类是 ChildFirstClassLoade…

Nginx 安全加固:如何阻止 IP 直接访问,只允许域名访问

在部署网站或 Web 应用时,我们通常会通过域名来访问服务。然而,有时用户可能会尝试直接使用服务器的 IP 地址来访问,这不仅可能绕过我们的域名特定配置(如 SSL 证书、重定向规则等),还可能导致不必要的安全风险或管理混乱。本文将介绍如何配置 Nginx,使其在通过 IP 地址…

服务端处于 TIME_WAIT 状态的 TCP 连接,收到相同四元组的 SYN 后会发生什么?详解

文章目录一、先判断 SYN 是否合法1、开启「时间戳」机制1.1、合法 SYN1.2、非法 SYN2、关闭「时间戳」机制1.1、合法 SYN1.2、非法 SYN二、收到合法 SYN三、收到非法 SYN一、先判断 SYN 是否合法 1、开启「时间戳」机制 1.1、合法 SYN 客户端的 SYN「序列号」比服务端「期望…

数字化转型:一文读懂从单系统到智能架构(业务、应用、数据、技术架构)的跨越

在数字化浪潮席卷全球的今天&#xff0c;企业正经历从 “单系统孤岛” 到 “智能架构协同” 的范式革命。智能架构以业务敏捷化、应用服务化、数据价值化、技术云原生化为核心特征&#xff0c;通过四个维度的架构升级&#xff0c;破解传统 IT 系统的效率瓶颈&#xff0c;支撑企…

AUTOSAR进阶图解==>AUTOSAR_SRS_Transformer

AUTOSAR Transformer 详解 基于AUTOSAR 4.4.0标准的Transformer模块分析与说明目录 1. Transformer概述 1.1 Transformer的作用1.2 Transformer的基本特性 2. Transformer架构 2.1 整体架构2.2 类层次结构 3. Transformer类型 3.1 SOME/IP Transformer3.2 COM Based Transform…