【Java】—— 堆

一、堆的定义

在计算机科学中,堆(heap)是一种特殊的树状数据结构。用于存储和管理数据。堆通常用于实现优先队列。其中具有最高(或最低)优先级的元素始终位于堆的根部。

堆分为最小堆和最大堆两种类型:

  • 最小堆:父节点的值小于或等于其子节点的值。
  • 最大堆:父节点的值大于或等于其子节点的值。

二、堆的特性

1、堆是一个完全二叉树,通过数组的方式来存储的完全二叉树。将这个树层序遍历,遍历的结果依次添加到数组中,遇到 空节点 则将 空节点 也添加到数组中

放进 null 是为了维护父子之间的下标关系,可以通过下标换算出父子关系

设 父节点下标为 parent ,左子树的下标就是 2*parent+1 ,右子树的下标就是 2*parent+2

设 子节点下标为 child 父节点的下标(child - 1) / 2

(普通的二叉树可能包含大量的 null 导致内存利用率低所以不使用数组表示)

2、对于堆来说,任何一个节点,值都小于(或大于)两个子节点的值,两个子节点的值谁大谁小没关系。树的树根节点( 数组下标为[0] )的元素,就是整个树的 最小值/最大值。

三、堆的实现

1、向下调整/下沉式调整

有一个数组,除了 [0] 元素之外,其他的元素都满足小堆的要求

针对上述情况,就需要用到 向下调整

第一步:先确定要比较的节点是哪一个,如果只有左子树,就直接和左子树比,如果同时有左右子树,先比较左右子树中更小的那一个

第二步:拿着更小的节点和根节点进行比较,如果根节点比子节点大,交换两个节点的值。

2.构建堆的基本思路

从最后一个非叶子节点出发,从后往前遍历,针对每个节点进行向下调整

package PriorityQueue;

import java.util.Arrays;

public class Heap {
    /**
     *向下调整
     * @param array 要调整的堆
     * @param size 堆的大小
     * @param index 从哪个位置开始向下调整
     */
    public static void shiftDown(int[] array,int size,int index){
        //要调整的父节点
        int parent = index;
        //计算要调整的左子树节点
        int child = 2 * parent + 1;
        //如果子节点下标超出数组范围,说明子节点不存在
        //主循环就结束,调整就完成了
        while (child < size){
            //左子树+1得到右子树,通过这个判断找出左右子树更小的值
            if(child + 1 < size && array[child + 1] < array[child]){
                child = child + 1;
            }
            //然后比较 child 和 parent 的大小关系
            if(array[parent] <= array[child]){
                break;
                //父节点比子节点小,已经符合小堆规则,不需要再调整了
            } else {
              //交换父子的值
              int tmp = array[parent];
              array[parent] = array[child];
              array[child] = tmp;
              //更新 parent 和 child 的指向
              parent = child;
              child = 2 * parent + 1;
            }
        }
    }

    /**
     * 建堆操作
     * @param array
     */
    public static void creatHeap(int[] array){
        //1.找到最后一个非叶子节点
        //2.从后往前循环进行向下调整
        int lastLeaf = array.length - 1;
        int lastParent = (lastLeaf - 1) / 2;
        for(int i = lastParent;i>=0;i--){
            shiftDown(array,array.length,i);
        }
    }

    /**
     * 向上调整堆,整个数组只有最后一个元素不满足堆的要求
     * 在这个前提下,堆最后一个元素进行向上调整
     * @param array
     * @param index
     */
    public static void shiftUp(int[] array,int index){
        int child = index;
        int parent = (child - 1) / 2;
        //child为0,说明已经到根节点了,循环就结束了
        while (child > 0){
            if(array[parent] > array[child]){
                //此时应该要进行交换
                int tmp = array[parent];
                array[parent] = array[child];
                array[child] = tmp;
                //更新 child 和 parent 的指向
                child = parent;
                parent = (child - 1) / 2;
            }else {
                //此时符合堆,不需要再调整了
                break;
            }
        }
    }

    public static void main(String[] args) {
        int[] array = {1,3,2,6,5,7,8,9,10,0};
        creatHeap(array);
        System.out.println(Arrays.toString(array));
    }
}

可以看到最后的结果已经满足堆的要求了,整体的建堆时间复杂度是 O(N)

四、优先级队列

队列的核心操作:

1.入队列

        新添加的元素可能会打破原有的堆的规则,针对新来的元素就需要进行 “向上调整”

    /**
     * 向上调整堆,整个数组只有最后一个元素不满足堆的要求
     * 在这个前提下,堆最后一个元素进行向上调整
     * @param array
     * @param index
     */
    private static void shiftUp(int[] array,int index){
        int child = index;
        int parent = (child - 1) / 2;
        //child为0,说明已经到根节点了,循环就结束了
        while (child > 0){
            if(array[parent] > array[child]){
                //此时应该要进行交换
                int tmp = array[parent];
                array[parent] = array[child];
                array[child] = tmp;
                //更新 child 和 parent 的指向
                child = parent;
                parent = (child - 1) / 2;
            }else {
                //此时符合堆,不需要再调整了
                break;
            }
        }
    }


public class MyPriorityQueue {
    private int[] arrar;
    public int size = 0;
    public MyPriorityQueue(){
        arrar = new int[1000];
    }
    //入队列
    public void offer(int value){
        if(size == arrar.length){
            //这里还可以实现一个扩容功能
        }
        //先把新元素进行尾插
        arrar[size] = value;
        size++;
        //从最后的元素开始向上进行调整
        Heap.shiftUp(arrar,size - 1);
    }
}

2.出队列

队首就是 [0] 元素,需要把这个元素删除掉,但是在堆中,直接删掉根节点,后续的节点往期走就有可能导致堆的规则发生改变。而最后一个节点删除不会造成其他影响,所以可以通过先交换首尾的值,再对尾元素删除,最后对首进行向下调整。

//出队列
    public Integer poll(){
        if(size == 0){
            return null;
        }
        //先把第一个元素和最后一个元素交换
        int ret = arrar[0];
        arrar[0] = arrar[size - 1];
        //这一步写不写无所谓,因为size--就把这个元素删了
        arrar[size - 1] = ret;
        size--;
        Heap.shiftDown(arrar,size,0);
        return ret;
    }

3.取队首元素

    public Integer peek(){
        if(size == 0){
            return null;
        }
        return arrar[0]; 
    }

测试

public class text2 {
    public static void main(String[] args) {
        MyPriorityQueue queue = new MyPriorityQueue();
        queue.offer(3);
        queue.offer(4);
        queue.offer(2);
        System.out.println(queue.poll());
        System.out.println(queue.poll());
        System.out.println(queue.poll());
    }
}

4.给优先级队列指定比较规则

Java标准库中有这样两个接口:ComparableComparator 就是用来描述比较规则的

Comparable : 如果队列插入的是类的对象,就可以让这个类实现 Comparable 接口

(内部制定规则,比较自己和别人)

Comparator : 如果队列插入的是 int 或 String 这种,此时就可以实现 Comparator 来完成上述规则

(第三方,比较两个内容)

写法一:

                                //泛型参数取决于你要比较什么
class IntComparator implements Comparator<Integer>{
    @Override
    public int compare(Integer o1, Integer o2) {
        //此处是自定义比较规则,原来的规则就不要了
        //如果判断 o1 比 o2 小,就返回一个 < 0 的整数
        //如果判断 o1 比 o2 大,就返回一个 > 0 的整数.
        //如果判断 o1 比 o2 相等,就返回 0
        //这里就是定义了大堆的比较规则,o1 - o2就是小堆
        return o2 - o1;
    }
}

写法二:

//这个写法是“匿名内部类”写法,实现了 Comparator 接口,重写了 Comparator 的 compare 方法,同时创建了内部类的实例
        //只能创建一次!!
        PriorityQueue<Integer> queue = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });

使用优先级队列保存自定义的类型

package PriorityQueue;

import java.util.PriorityQueue;


class Student implements Comparable<Student>{

    public int id;
    public String name;
    public int score;

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

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

    //对于 Comparator 来说,是有两个参数,比较 o1 和 o2 的关系
    //对应 Comparable 来说,看起来是一个参数,实际上是拿 this 和 o 进行比较
    //使用 Comparable 的前提就是这个类是你自己写的
    @Override
    public int compareTo(Student o) {
        //大堆的写法,比较分数
        return o.score - this.score;
    }
}


public class text3 {
    public static void main(String[] args) {
        PriorityQueue<Student> queue = new PriorityQueue<>();
        queue.offer(new Student(1,"Alice",88));
        queue.offer(new Student(2,"Bob",90));
        queue.offer(new Student(3,"Charlie",70));
        queue.offer(new Student(4,"David",85));
        System.out.println(queue.toString());
    }
}

还可以同时写多种 Comparator 种比较方法

    class StudentComparator1 implements Comparator<Student>{
        @Override
        public int compare(Student o1,Student o2){
            return o2.score - o1.score;
        }
    }
    class StudentComparator2 implements Comparator<Student>{
        @Override
        public int compare(Student o1,Student o2){
            return o2.id - o1.id;
        }
    }
    class StudentComparator3 implements Comparator<Student>{
        @Override
        public int compare(Student o1,Student o2){
            return o1.name.compareTo(o2.name);
        }
    }

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

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

相关文章

Windows 使用 Docker + WSL2 部署 Ollama(AMD 显卡推理)搭建手册‌

Windows 使用 Docker WSL2 部署 Ollama&#xff08;AMD 显卡推理&#xff09;搭建手册‌ ‌手册目标‌ 在 Windows 11 上通过 ‌Docker WSL2‌ 调用 AMD 显卡运行 Ollama 推理服务。 实现 ‌低延迟、高性能的本地模型推理‌&#xff0c;同时不影响 Windows 正常使用。 标记…

【每天认识一个漏洞】shiro反序列化漏洞

&#x1f31d;博客主页&#xff1a;菜鸟小羊 &#x1f496;专栏&#xff1a;Linux探索之旅 | 网络安全的神秘世界 | 专接本 | 每天学会一个渗透测试工具 以下是在实际业务中遇到的一个漏洞&#xff0c;仅用来学习&#xff0c;通过暴露的 /actuator/heapdump 端点获取 Shiro key…

【AI大模型】DeepSeek + Kimi 高效制作PPT实战详解

目录 一、前言 二、传统 PPT 制作问题 2.1 传统方式制作 PPT 2.2 AI 大模型辅助制作 PPT 2.3 适用场景对比分析 2.4 最佳实践与推荐 三、DeepSeek Kimi 高效制作PPT操作实践 3.1 Kimi 简介 3.2 DeepSeek Kimi 制作PPT优势 3.2.1 DeepSeek 优势 3.2.2 Kimi 制作PPT优…

音频3A测试--AGC(自动增益)和NS(降噪)测试

一、测试前期准备 一台电脑&#xff1a;用于作为控制播放和录制数据&#xff1b; 一台音频处理器(调音台)&#xff1a;控制每个通道播放的数据&#xff0c;如噪声、人工头、模拟设备B输入的数据、收集标准麦克风&#xff0c;设备A处理完成的数据&#xff1b; 四个高保真音响&…

zabbix配置邮件告警

目录 实现步骤&#xff1a; 实现目的&#xff1a; 1.在监控端操作&#xff1a; 2.web界面部署 ​​​​​​​实现步骤&#xff1a; 1、在 zabbix服务端配置邮件发送脚本和修改 zabbix服务端配置文件; 2、在 zabbix前端控制台进行相关设置。 实现目的&#xff1a; Zab…

PHP fastadmin 学习

安装php环境安装mysql插件 修改 php.ini下载 phpstudy、fastadmin 错误 安装FastAdmin could not find driver 参考链接 安装插件 创建1.php <? phpinfo(); ?>运行 http://127.0.0.1/1.php 查看 POD 页面访问404 伪静态 Apache <IfModule mod_rewrite.c> O…

PARETO PROMPT OPTIMIZATION

题目 帕累托提示优化 论文地址&#xff1a;https://openreview.net/forum?idHGCk5aaSvE 摘要 自然语言迅速优化或及时工程已成为一种强大的技术&#xff0c;可以解锁大型语言模型&#xff08;LLMS&#xff09;的各种任务的潜力。尽管现有方法主要集中于最大化LLM输出的单一特…

Agent智能体是什么?

文章目录 一、Agent的起源与发展1.1时间线1.2核心驱动力 二、Agent的定义与架构2.1基本定义2.2典型结构&#xff08;以GPTs为例&#xff09; 三、OpenAI的Agent演进路径3.1关键阶段3.2技术支撑3.3 GPTs生态经济模型 四、其他Agent平台对比五、Agent实践案例5.1文本处理自动化5.…

【Linux第三弹】Linux基础指令 (下)

目录 &#x1f31f;1.find指令 1.1find使用实例 ​编辑 &#x1f31f;2.which指令 &#x1f31f;3.grep指令 3.1grep使用实例 &#x1f31f; 4.zip/unzip指令 4.1 zip/unzip使用实例 &#x1f31f;5.tar指令 5.1 tar使用实例 &#x1f31f;6.完结 很庆幸走在自己…

【Laplacian边缘检测详解】

Laplacian边缘检测详解 目录 Laplacian边缘检测详解一. 定义二. 原理三. 特点四. 使用技巧五. MATLAB示例代码示例1&#xff1a;基本Laplacian边缘检测示例2&#xff1a;扩展Laplacian核的使用示例3&#xff1a;与Sobel边缘检测的比较示例4&#xff1a;检测图像中的文字边缘示例…

为什么要学习数据结构与算法

今天&#xff0c;我向大家介绍一门非常重要的课程——《数据结构与算法》。这门课不仅是计算机学科的核心&#xff0c;更是每一位开发者从“小白”迈向“高手”的必经之路。 1、为什么要学习数据结构与算法 总的来说&#xff0c;数据结构与算法是&#xff1a; 求职的“敲门砖”…

【第13节】C++设计模式(行为模式)-Template(模板)模式

一、问题的提出 Template 模式&#xff1a;算法步骤框架与细节实现的分离 假设我们正在开发一个文档处理系统&#xff0c;需要支持多种文档格式的导出&#xff08;如 PDF、Word、HTML 等&#xff09;。每种文档格式的导出过程大致相同&#xff0c;都包含以下步骤&#xff1a; …

安卓binder驱动内核日志调试打印开放及原理(第一节)

背景&#xff1a; 经常有学员朋友在做系统开发时候&#xff0c;有时候遇到binder相关的一些问题&#xff0c;这个时候可能就需要比较多的binder相关日志&#xff0c;但是正常情况下这些binder通讯的的内核日志都是没有的打印的&#xff0c;因为经常binder通讯太过于频繁&#…

uniapp 常用 UI 组件库

1. uView UI 特点&#xff1a; 组件丰富&#xff1a;提供覆盖按钮、表单、图标、表格、导航、图表等场景的内置组件。跨平台支持&#xff1a;兼容 App、H5、小程序等多端。高度可定制&#xff1a;支持主题定制&#xff0c;组件样式灵活。实用工具类&#xff1a;提供时间、数组操…

Gpt翻译完整版

上一篇文章收到了很多小伙伴的反馈&#xff0c;总结了一下主要以下几点&#xff1a; 1. 说不知道怎么调api 2. 目前只是把所有的中文变成了英文&#xff0c;如果想要做多语言还需要把这些关键字提炼出来成放到message_zh.properties和message_en.properties文件中&#xff0c…

【MATLAB例程】三维下的IMM(交互式多模型),模型使用CV(匀速)、CT(匀速转弯)和CA(匀加速),滤波使用EKF。附完整代码

本文介绍一个三维IMM(Interacting Multiple Model)算法,该算法用于目标跟踪,结合了不同运动模型(匀速、匀加速和转弯)。代码使用MATLAB编写,包含仿真、模型预测和结果可视化。订阅专栏后,可直接获得完整代码 文章目录 运行结果完整代码代码解析1. 初始化环境2. 仿真参数…

未来经济范式争夺战:AR眼镜为何成为下一代交互终端的制高点?

未来经济范式争夺战&#xff1a;AR眼镜为何成为下一代交互终端的制高点&#xff1f; 在蒸汽机轰鸣的工业革命时代&#xff0c;煤炭、铁路、电报构建了第一个现代经济范式&#xff1b;互联网时代&#xff0c;电力、光纤、物流网络重构了全球经济版图。当前&#xff0c;我们正站…

【Python爬虫】爬取公共交通路网数据

程序来自于Github&#xff0c;以下这篇博客作为完整的学习记录&#xff0c;也callback上一篇爬取公共交通站点的博文。 Bardbo/get_bus_lines_and_stations_data_from_gaode: 这个项目是基于高德开放平台和公交网获取公交线路及站点数据&#xff0c;并生成shp文件&#xff0c;…

如何将飞书多维表格与DeepSeek R1结合使用:效率提升的完美搭档

将飞书的多维表格与DeepSeek R1结合使用&#xff0c;就像为你的数据管理和分析之旅装上一台涡轮增压器。两者的合作&#xff0c;不仅仅在速度上让人耳目一新&#xff0c;更是将智能化分析带入了日常的工作场景。以下是它们如何相辅相成并改变我们工作方式的一些分享。 --- 在…

一周学会Flask3 Python Web开发-在模板中渲染WTForms表单视图函数里获取表单数据

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 为了能够在模板中渲染表单&#xff0c;我们需要把表单类实例传入模板。首先在视图函数里实例化表单类LoginForm&#xff0c;然…