基础数据结构 -- 堆

news2024/9/20 5:37:38

1. 简介

        堆可以看做是一种特殊的完全二叉树,它满足任意节点的值都大于或小于其子节点的值。

2. 功能

  • 插入元素:插入新元素时,先将元素放至数组末尾,然后通过上浮算法自底向上调整,使堆保持性质。
  • 删除堆顶元素:删除堆顶元素后,将最后一个元素移至堆顶,然后通过下沉算法自顶向下调整,重新满足堆的性质。
  • 建堆过程:从倒数第一个非叶子节点开始,对每个子树进行向下调整,直至整棵树满足堆的性质。

3. 图解

堆结构:

插入元素:

删除堆顶元素:

4. 实现

        在Java中,堆通常通过数组实现,利用数组的索引来表示节点之间的关系。

具体实现方式如下:

  • 定义一个数组来存储堆的元素。
  • 使用数组下标表示节点的位置,例如根节点的下标为0,左子节点的下标为2 * i + 1,右子节点的下标为2 * i + 2。
  • 在插入元素时,将新元素插入到数组末尾,然后通过上浮操作将其移动到正确的位置。
  • 在删除元素时,将最后一个元素替换到堆顶,然后通过下沉操作将其移动到正确的位置。
  • 堆排序时,将堆顶元素与最后一个元素交换,然后将堆的大小减一,再通过下沉操作将新的堆顶元素移动到正确的位置。

 小根堆代码实现:

public class MinHeap {
    private int[] data; // 存储堆元素的数组
    private int size; // 堆的大小

    public MinHeap(int capacity) {
        data = new int[capacity];
        size = 0;
    }

    // 返回堆的大小
    public int getSize() {
        return size;
    }

    // 返回堆是否为空
    public boolean isEmpty() {
        return size == 0;
    }

    // 返回父节点的索引
    private int parent(int index) {
        return (index - 1) / 2;
    }

    // 返回左子节点的索引
    private int leftChild(int index) {
        return 2 * index + 1;
    }

    // 返回右子节点的索引
    private int rightChild(int index) {
        return 2 * index + 2;
    }

    // 上浮操作
    private void siftUp(int index) {
        while (index > 0 
                    && data[parent(index)] > data[index]) {
            swap(parent(index), index);
            index = parent(index);
        }
    }

    // 下沉操作
    private void siftDown(int index) {
        while (leftChild(index) < size) {
            int minIndex = leftChild(index);
            if (rightChild(index) < size 
                    && data[rightChild(index)] < data[minIndex]) {
                minIndex = rightChild(index);
            }
            if (data[index] <= data[minIndex]) {
                break;
            }
            swap(index, minIndex);
            index = minIndex;
        }
    }

    // 交换两个元素的位置
    private void swap(int i, int j) {
        int temp = data[i];
        data[i] = data[j];
        data[j] = temp;
    }

    // 向堆中插入元素
    public void insert(int value) {
        if (size == data.length) {
            throw new IllegalStateException("Heap is full");
        }
        data[size] = value;
        siftUp(size);
        size++;
    }

    // 从堆中删除最小元素(即堆顶元素)
    public int extractMin() {
        if (isEmpty()) {
            throw new IllegalStateException("Heap is empty");
        }
        int minValue = data[0];
        data[0] = data[size - 1];
        size--;
        siftDown(0);
        return minValue;
    }
}

也有一个偷懒的办法,我们可以通过 Java 内置的 PriorityQueue 类来实现最小堆。通过调用add() 方法可以向堆中插入元素,而通过调用 poll() 方法可以从堆中删除最小元素(即堆顶元素)。

需要注意的是,PriorityQueue  默认实现的是最小堆,如果需要实现最大堆,可以通过传递自定义比较器来实现。例如,使用 PriorityQueue<Integer>(Conleections.reverseOrder()) 来创建一个最大堆。

public class HeapTest {
    public static void main(String[] args) {
        // 创建一个最小堆
        PriorityQueue<Integer> minHeap = new PriorityQueue<>();

        // 向堆中插入元素
        minHeap.add(5);
        minHeap.add(2);
        minHeap.add(8);
        minHeap.add(1);
        minHeap.add(3);

        System.out.println("最小堆的元素: " + minHeap); // 输出: [1, 2, 8, 5, 3]

        // 从堆中删除最小元素(堆顶元素)
        int minElement = minHeap.poll();
        System.out.println("被删除的最小元素: " + minElement); // 输出: 1

        System.out.println("删除最小元素后的最小堆: " + minHeap); // 输出: [2, 3, 8, 5]
    }
}

7. 应用场景

  • 堆排序:利用最大堆或最小堆的性质,可以进行高效的排序操作。堆排序是一种时间复杂度为O(n log n)的排序算法,它通过建堆和反复删除堆顶元素进行排序。
  • 中位数查询:使用最大堆和最小堆可以在O(log n)的时间内查询一组数据的中位数,这对于实时数据分析等领域非常重要。
  • Top K问题:对于一个数据流,需要在其中找到前K大或前K小的元素,可以使用堆来实现。维护一个大小为K的堆,遍历数组,从数组中取出数据与堆顶元素比较,如果比堆顶元素大,则将堆顶元素删除,并将这个元素插入到堆中;如果比堆顶元素小,则不做处理,继续遍历数组。这样等数组中的数据都遍历完之后,堆中的数据就是前K大数据了。

6. 总结

        综上所述,堆是Java中一种非常重要的基础数据结构,它提供了高效的数据插入和删除操作,广泛应用于堆排序、top k问题等场景。理解堆的基本操作和应用,对于开发者来说非常有益,有助于更好地利用这一数据结构来解决实际问题。

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

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

相关文章

Unity DOTS技术(九) BufferElement动态缓冲区组件

文章目录 一.简介二.例子 一.简介 在之前的学习中我们发现Entity不能挂载相同的组件的. 当我们需要用相同的组件时则可以使用.IBufferElementData接口 动态缓冲区组件来实现 二.例子 1.创建IBufferElementData组件 using Unity.Entities; using UnityEngine; //[GenerateAu…

BIOPLUSS引领膳食行业创新、整合与再造

2024年NHNE如期而至&#xff0c;同时今年也是中挪建交70年周年&#xff0c;BIOPLUSS作为挪威品牌代表也参加了此次NHNE国际健康营养博览会&#xff0c;此次NHNE展会吸收了来自30多个国家及地区的1200多家品牌参与&#xff0c;BIOPLUSS同时受挪威领事馆、挪威创新署邀请&#xf…

安卓事件交互(按键事件、触摸事件、手势识别、手势冲突处理)

本章介绍App开发常见的以下事件交互技术&#xff0c;主要包括&#xff1a;如何检测并接管按键事件&#xff0c;如何对触摸事件进行分发、拦截与处理&#xff0c;如何根据触摸行为辨别几种手势动作&#xff0c;如何正确避免手势冲突的意外状况。 按键事件 本节介绍App开发对按…

Duck Bro的第512天创作纪念日

Tips&#xff1a;发布的文章将会展示至 里程碑专区 &#xff0c;也可以在 专区 内查看其他创作者的纪念日文章 我的创作纪念日第512天 文章目录 我的创作纪念日第512天一、与CSDN平台的相遇1. 为什么在CSDN这个平台进行创作&#xff1f;2. 创作这些文章是为了赚钱吗&#xff1f…

Nginx服务配置

一、Nginx服务的主配置文件nginx.conf vim /usr/local/nginx/conf/nginx.conf 全局块&#xff1a;全局配置&#xff0c;对全局生效&#xff1b;events块&#xff1a;配置影响 Nginx 服务器与用户的网络连接&#xff1b;http块&#xff1a;配置代理&#xff0c;缓存&#xff0c…

WordPress 高级缓存插件 W3 Total Cache 开启支持 Brotli 压缩算法

今天明月给大家分享一下 WordPress 高级缓存插件 W3 Total Cache 开启支持 Brotli 压缩算法的教程&#xff0c;在撰写【WordPress 高级缓存插件 W3 Total Cache Pro 详细配置教程】一文的时候明月就发现 W3 Total Cache 已经支持 Brotli 压缩算法了&#xff0c;可惜的是在安装完…

代码随想录 -数组

1.二分算法 边界开闭 左闭右闭 原则 这里的&#xff0c;middle不是要找的值。那么nums【middle】>tager 我们要更新右边界为middle-1 &#xff08;因为要左区间 所以更新右边界&#xff09; 在这里插入图片描述 class Solution { public:int search(vector<int>&a…

【Python】解决Python报错:IndexError: list index out of range

​​​​ 文章目录 引言1. 错误详解2. 常见的出错场景2.1 循环中的索引错误2.2 错误的列表操作 3. 解决方案3.1 使用安全的访问方法3.2 循环时使用正确的范围 4. 预防措施4.1 编写单元测试4.2 动态检查列表长度 结语 引言 在Python中操作列表时&#xff0c;IndexError: list …

【PowerDesigner】创建和管理CDM之新建实体

目录 &#x1f30a;1. PowerDesigner简介 &#x1f30d;1.1 常用模型文件 &#x1f30d;1.2 PowerDesigner使用环境 &#x1f30a;2. 创建和管理CDM &#x1f30d;​​​​​​2.1 新建CDM &#x1f30d;2.2 新建实体 &#x1f30a;1. PowerDesigner简介 &#x1f30d;1…

初阶 《函数》 2.C语言中函数的分类

2.C语言中函数的分类 1.库函数 2.自定义函数 2.1 库函数 为什么会有库函数&#xff1f; 1.我们知道在我们学习C语言编程的时候&#xff0c;总是在一个代码编写完成之后迫不及待的想知道结果&#xff0c;想把这个结果打印到我们的屏幕上看看。这个时候我们会频繁的使用一个功能…

AVL树 ---(C++)

本篇讲全面的讲解 AVL 树的插入&#xff0c;旋转以及验证 AVL 树的性能&#xff08;本篇未实现删除代码&#xff09;。至于为什么会有 AVL 树&#xff0c;这是因为简单的二叉搜索树并不能直接的保证搜索的效率&#xff0c;因为当我们在二叉搜索树中插入一段有序的序列的时候&am…

SpringBoot 配置事务

SpringBoot 在启动时已经加载了事务管理器&#xff0c;所以只需要在需要添加事务的方法/类上添加Transactional即可生效&#xff0c;无需额外配置。 TransactionAutoConfiguration 事务的自动配置类解析&#xff1a; SpringBoot 启动时加载/META-INF/spring/org.springframewor…

kafka集成spark

1.新建Scala项目 具体教程可见在idea中创建Scala项目教程-CSDN博客 1.1右键项目名-添加框架支持-勾选scala 1.2main目录下新建scala目录-右键Scala目录-将目录标记为-勾选源代码根目录 1.3创建包com.ljr.spark 1.4引入依赖&#xff08;pox.xml) <dependencies><…

Java——二维数组

一、二维数组介绍 二维数组与一维数组很相似。可以说二维数组是元素为一维数组的数组&#xff0c;也就是一维数组的数组。每个元素可以通过行索引和列索引来访问。 1、二维数组的创建 我们知道&#xff0c;在 C 语言中&#xff0c;二维数组是一个连续的内存块&#xff0c;通…

【Python】使用pip安装seaborn sns及失败解决方法与sns.load_dataset(“tips“)

&#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主。 &#x1f913; 同时欢迎大家关注其他专栏&#xff0c;我将分享Web前后端开发、人工智能、机器学习、深…

RISC-V MCU IDE MRS(MounRiver Studio)开发 编译后打印FLASH及RAM使用占比信息

以RISC-V MCU IDE MounRiver Studio(MRS)为例&#xff0c;首先我们选中目标工程&#xff0c;点击工具栏工程属性按钮&#xff0c;打开工程属性配置页&#xff1a; 在C/C Build->Settings->Tool Settings选项列表中单击GNU RISC-V Cross C Linker->Miscellaneous&#…

Anzo 跟单社区现已正式上线!即刻体验无与伦比的强大功能

Anzo 跟单社区现已正式上线! ANZO 跟单社区是一个颠覆性的创新跟单社区平台&#xff0c;作为新一代跟单社区&#xff0c;我们旨在让更多的用户享受跟单交易带来的便捷性和收益性。交易者可以通过跟单社区&#xff0c;学习和分享交易策略&#xff0c;轻松复制交易专家的交易策略…

人类记忆优化算法:针对全局优化问题的记忆启发优化器

Human memory optimization algorithm: A memory-inspired optimizer for global optimization problems 24年 Expert Systems With Applications sci一区 原文链接: https://doi.org/10.1016/j.eswa.2023.121597 Zhu D, Wang S, Zhou C, et al. Human memory optimization alg…

二进制文件的膨胀策略和使用 debloat 消除膨胀测试

在恶意软件的分析中有的 Windows 可执行文件&#xff08;PE 文件&#xff09;会通过膨胀策略来绕过防病毒一些防病毒的检查&#xff0c;比如上传云进行分析&#xff0c;因为文件太大了所以无法进行一些防病毒分析。一般的可执行文件有很多的膨胀策略&#xff0c;一般简单的膨胀…

Elasticsearch-经纬度查询(8.x)

目录 一、开发环境 二、pom文件 三、ES配置文件 四、ES相关字段 五、ES半径查询 ES的字段类型:geo_point&#xff0c;可以实现以一个点为中心的半径查询(geo_distance query) ES 地里位置查询: 半径查询(geo_distance query)查询指定矩形内的数据(geo_bounding_box quer…