堆(Heap)&优先级队列(Priority Queue)

ptrade从零开始学习量化交易第166期【ptrade量化策略研究参考--三个因子挖掘思路】 三个因子挖掘思路 阅读详情

一、堆的概念

堆(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

UFS总体介绍及FPGA开发 通过实现UFS的主控制器、设备控制器等组件,我们可以在FPGA上进行UFS开发,并实现高速数据传输和低功耗特性的存储解决方案。物理层负责传输物理信号,数据链路层提供数据包的传输和错误检测功能,传输层处理数据包的分段、重组和重传,逻辑层管理设备和主控制器之间的命令和数据传输,文件系统层负责文件管理和数据存储。在本文中,我们将介绍UFS的总体架构和特性,并探讨如何在FPGA上进行UFS开发。这些组件的实现涉及到UFS协议的具体细节和硬件平台的特性,因此需要参考UFS协议规范和FPGA开发平台的相关文档。 阅读详情

相关推荐

11-4 AEB 算法原理及系统组成

本专题旨在探讨如何利用仿真软件开发自动紧急制动系统AEB,主要通过整理开发流程,实现从需求到验证的全流程掌握,为后续开发提供支持;同时,研究了如何在汽车自动驾驶仿真工具中开发自己的算法,并解决了从ISO、E-CNAP、GB/T以及交通法规等提炼出功能需求的问题,以快速实现原型模型的开发。本专题重点讲解了AEB的工作原理、系统/软件架构和需求、算法模型及实现、CCRs/CCRb/CCRm建模与仿真、VRU-AEB-B/P/M建模与仿真,同时还提供了测试用例,期待将AEB算法成功地产品化。

AIgraphX 8551

数据结构——优先级队列和堆

优先级队列、堆及其区别

xh1605004242的博客 3377

NVIDIA DRIVE SDK学习(二)——NvMedia

可以通过使用NvSciBuf API通过NvMedia Tensor属性分配NvSciBuf来创建NvMedia张量。由于NvSciBuf API有助于NvMedia和NVIDIA®CUDA®之间的数据共享,这使得分配的张量可以在NvSciBuf API允许的情况下重复使用。它支持图像聚合操作。NvSciSync组件使得NvMedia组件可以与其他组件保持同步,比如CUDA组件、基于NvSciSync的组件等。立体视差模式:在这种模式下,该模块通过左图像和右图像来产生立体视差,从而进行物体深度测量。

weixin_39592381的博客 3045

《算法系列》之队列与堆

队列和堆也是基础的数据结构,无论刷不刷题都需要了解的数据结构。

小夏陌的博客 1418

告别F5失灵!手把手教你为Cursor配置.Net Core控制台项目专属调试环境

本文详细指导如何在Cursor编辑器中为.Net Core控制台项目配置专属调试环境,解决F5失灵等常见问题。从调试器选择到launch.json配置,再到高级调试技巧,帮助开发者高效搭建完整的调试工具链,提升开发效率。

weixin_28834765的博客 285

vos如何对接okcc(保姆级教程)

1.添加账户 点击 系统->账户管理 [图片] 2.创建费率 系统->费率管理 点击添加

qq_46333281的博客 3453

数据结构学习 -- 堆和优先队列

堆和优先队列 二叉堆 堆通常使用了树的存储方式,比较常用的堆是:二叉堆,也就是满足一些特殊性质的二叉树。 二叉堆的性质 1️⃣ 二叉堆必须是一颗完全二叉树,所谓完全二叉树可以理解为将元素按顺序一层一层的排列成二叉树的形状,完全二叉树的特点为: 完全二叉树是一颗叶子节点只能出现在最下面两层的二叉树。 完全二叉树最下面一层的叶子节点必须从左至右连续出现,中间不能出现空节点。 完全二叉树倒数第二层的如果有叶子节点,一定位于右边连续位置。 完全二叉树中如果节点只有一个子节点,那么一定是左子节点,完全二叉树中不存

weixin_48084293的博客 1869

【开源】基于TI ADS1299与ESP32的8通道脑电采集设备YuEEG全解析

本文详细解析了基于TI ADS1299芯片与ESP32主控的开源8通道脑电采集设备YuEEG。内容涵盖硬件设计、固件驱动开发及Python上位机软件实现,提供了从电路连接、SPI通信配置到数据读取与实时波形显示的全套教程。该项目为学习脑机接口(BCI)与高精度生物电信号采集提供了完整的开源解决方案,适合创客、学生及嵌入式开发者实践。

weixin_42576410的博客 1241

堆 和 优先级队列(超详细讲解,就怕你学不会)

有兴趣学数据结构了吗?

LEE180501的博客 2002

触摸芯片RM1015B基本使用方法

RM1015B触摸芯片使用指南:该芯片采用4通道设计(实际使用3通道),通过I2C接口通信(设备地址0x2B)。硬件设计需注意中断/复位引脚复用,需上拉处理。软件配置包括初始化、中断处理和定时器扫描,通过设置各通道阈值(0-300W)调节灵敏度,值越小越灵敏。调试时可调用rm1015B_debug_func()输出波形数据。典型应用场景包括接近检测和手势识别,中断触发后需在8ms内响应。特别注意不同通道的寄存器映射关系及校准流程,确保检测准确性。

云淡风轻 176

【GameFramework框架】三、快速启动

这篇文章就带领大家如何快速启动GameFramework框架。

优快云博客专家,Unity3D高级软件工程师,《Unity 3D从入门到实战》作者。 4427

堆和优先队列

1.优先队列的概念 优先队列和其实是队列的一种 普通队列:先进先出;后进先出 优先队列:出队顺序和入队顺序无关;和优先级相关 优先队列的接口和普通队列的接口是完全相同的,只是在出队和查看队首的实现方式会不同(优先级最高的在队首); 优先队列可以使用不同的底层数据结构来实现   我们对队列这种抽象的数据结构做一些限制,创造出优先队列这个概念,我们在实现这个概念时可以使用不同的底...

哈喽羊 1万+

一文搞懂 JavaScript 模块化规范:CommonJS、AMD、ES6 Module

在现代 JavaScript 开发中,模块化是提升代码组织性和可维护性的关键。本文深入解析三种主流的模块化规范——CommonJS、AMD 和 ES6 Module。

Allen- 3479

优先级队列priority_queue

使用这种方式,需要显示定义优先级队列的容器类型和比较函数//函数对象类 template < typename T > class cmp {public ://重载 () 运算符 bool operator()(T a , T b) {} };int x;int y;};1 , 2 };0 , 2 };1 , 3 };2 , 5 };3 , 6 };// 显式指定容器类型和比较函数 pq . push(a);while(!}.........

weixin_47266712的博客 1万+

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

C++效率掌握之STL库:优先级队列priority_queue && 双端队列deque

本篇是STL库专题之priority_queue和deque,书接上文

Zero_VPN的博客 3397

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

堆和 priority_queue

在算法竞赛中,如果是需要使用堆的题目,我们一般就直接用现成的 priority_queue,很少手写一个堆,因为这样比较省事。而在优先级队列中,元素被赋予优先级,当插入元素时,同样是在队尾,但是会根据优先级进行位置调整,优先级越高,调整后的位置越靠近队头;堆中的所有运算,比如建堆,向堆中插入元素以及删除元素等,都是基于堆中的两个核心操作实现的——向上调整算法以及向下调整算法。因此,在实现堆之前,我们先来掌握两种核心操作。(2)交换完之后,重复 1 操作,直到比儿子结点的权值都大,或者换到叶节点的位置。

shdbdndj的博客 1659

算法竞赛里面的STL——堆和priority_queue

此外,双向链表还能迅速定位到删除操作后新相邻的元素,便于我们判断它们是否为未出列的异性,并据此更新堆中的配对信息。那我为什么不建一个大根堆,只要我保证这个堆的大小是k,这样子堆顶的元素就会一直是第k小的。由于我们使用的是大根堆,所以20是优先级最高的元素。时间复杂度:由于底层是一个堆结构,所以删除优先级最高的元素的时间复杂度为O(log n),其中n是优先级队列中的元素数量。所谓第k大,我们完全可以创建一个小根堆,然后保持堆的大小是k,然后这样子就能保证heap.top()就是第k大的元素了。

2301_80224556的博客 1423

【C++】优先级队列priority_queue模拟实现&&仿函数

我们在vector讲解中已经了解到了priority_queue,只能说是浅谈,priority_queue底层到底是个啥勒?今天带大家揭晓它的面纱。⭐主体这里就创建两个文件priority_queue.h(头文件),test.cpp(测试代码文件)优先级队列priority_queue,即数据结构中的堆,堆是一种通过使用数组来模拟实现特定结构二叉树的二叉树的数据结构,根据父亲节点与孩子节点的大小关系,可以将堆分为大堆和小堆:大堆:所有父亲节点的值都大于或等于它的孩子节点的值。

AAlykk的博客 1275

优先级队列(堆) 与 Priority Queue

优先级队列(Priority Queue)是一种特殊的队列数据结构,它的核心特点是不再遵循“先进先出(FIFO)”的原则,而是根据元素的优先级来决定出队顺序。堆(Heap)是一种完全二叉树结构的数组对象,同时满足堆序性质,是实现优先级队列的核心数据结构。到这里我的分享就先结束了~,希望对你有帮助我是dylan 下次见~无限进步。

2402_89003542的博客 1299

【C++】—— priority_queue与仿函数

仿函数呦

yusjushd的博客 2288

priority_queue优先级队列

优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)优先队列被实现为容器适配器容器适配器即将特定容器类封装作为其底层容器类底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector。

bananawolf的博客 1783

【C++】-- STL容器适配器之priority_queue

priority_queue即优先级队列,是一种容器适配器,最大元素放在第一个。底层用堆实现,默认是大堆,因为默认大的优先级高,可随时插入元素,可快速查找最大元素,即优先级队列中第一个元素。优先级队列元素从特定容器的“尾部”弹出,其称为优先队列的顶部。底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问...

局部变量和全局变量 2737

Heap —— Priority Queue 【堆 / 优先队列】

前言 - 为堆的学习做准备 二叉树的顺序存储 前面所讲的二叉树,什么孩子表示法呀,还有 孩子双亲表示法啊,都是链式存储。 而现在讲的是:顺序存储一棵二叉树。   存储方式 使用数组保存二叉树结构,方式即将二叉树用层序遍历的方式放入数组中。 一般只适合表示完全二叉树,因为 非完全二叉树会有空间的浪费。【也就是说:如果使用顺序存储来存储一棵二叉树,那么,最好是完全二叉树,这样就不会有太多的空间被浪费】 这种方式的主要用法就是堆的表示。   下标关系 已知双亲(parent)的下标,则:

DarkAndGrey的博客 2078

【C++/STL深度剖析】priority_queue 最全解析(什么是priority_queue? priority_queue的常用接口有哪些?)

优先级队列 priority_queue 是容器适配器中的一种,常用来进行对数据进行优先级处理,比如优先级高的值在前面,这其实就是数据结构中的 堆,它俩本质上是一样东西,底层都是以数组存储的完全二叉树,不过优先级队列 priority_queue 中加入了 泛型编程 的思想,并且属于 STL 中的一部分。本就就来详细的讲解一下 priority_queue 是如何使用的!!

weixin_45031801的博客 5785

【C++】STL——容器适配器priority_queue(优先级队列)详解 及 仿函数的介绍和使用

深度剖析容器适配器priority_queue(优先级队列)的底层结构及其模拟实现,介绍什么是仿函数,及仿函数的简单应用

m0_70980326的博客 4636
上一篇: 队列Queue
下一篇: 接口自动化测试pytest框架
奈葵
博客等级 码龄4年 245粉丝 49原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值