【数据结构】一篇讲清楚什么是堆? 带图食用超详细~

news2024/11/15 17:50:27

目录

一、堆的概念

1.堆是一个完全二叉树

2.堆分为大根堆和小根堆。 

3.堆与优先级队列的关系

二、堆操作

1.向下调整

2.删除堆顶元素

 3.添加新元素

 4.构建堆

A:自底向上构建

 B:自顶向下构建

C:两种方式对比

三、尝试自己编程实现堆 

1.初始化堆和一些常用方法

2.向下调整

3.向上调整

4.动态扩容

5.添加元素和删除元素

 四、Java中堆常用接口

1.构造方法 

2.常用方法

五、堆的应用


一、堆的概念

1.堆是一个完全二叉树

a:什么是完全二叉树?

我们把二叉树的节点按顺序定义为:1、2、3、4、5.....那么它的结构就要是:

   

意思就是不能跳过某一节点构建树,要按顺序。

b:正因为它是完全二叉树,我们可以用连续的数组模拟二叉树

使用数组更方便操作元素,例如交换。

c:如何用数组表示完全二叉树?

根据二叉树的性质:节点的索引和子节点的索引有如下关系:

举个例子:对应值为3的节点,其对应的索引i=3-1=2,

其双亲节点:parent=(3-1)/2=1/2=0 ,索引0对应值为1的节点 √

其左节点:left=2*2+1=5,索引5对应值为6的节点√

其右节点:right=2*2+2=6,索引6超出数组范围,其右节点不存在√

我们拿索引,计算出来的也是索引


2.堆分为大根堆和小根堆。 

大根堆:根节点是所有元素的max,且每个子树也必须满足最大堆的性质。

小根堆:根节点是所有元素的min ,且每个子树也必须满足最小堆的性质。


3.堆与优先级队列的关系

       我们已经知道,队列是先进先出。但是我们想让优先级更高的先出呢?于是有了优先级队列,其中每个元素都有一个关联的优先级。优先级高的元素比优先级低的元素先出队。在优先级队列中,元素的出队顺序与它们进入队列的顺序无关,而是由优先级决定。

       而优先级队列的底层实现可以用堆这种结构,得益于堆顶总是优先级最高的元素。


二、堆操作

在学构建堆之前,我们要先学习向下调整(Heapify Down),这是构建堆时的重要操作。

1.向下调整

概念:

       向下调整(Heapify Down),是堆中的一种操作,用于维持堆的性质。当堆中的一个节点可能违反了堆的性质时,向下调整通过将该节点与其子节点比较,并根据堆的类型进行必要的交换,使得该节点以及整个堆重新符合堆的性质。

概念很生硬哈,我来举例子~~ 

       可以看到,当左右子树已经实现堆结构时,向下调整效率很高~~

       这里先不用纠结那么多,先理解这个操作就好,关于构建堆,下面会讲。

       向下调整可以比喻为,某个元素的位置“德不配位”,站的太高了,我们要把它下沉,至于会降到哪里,就看它有多少水分了,挤干水分,回到它本应该在的位置。

时间复杂度分析: 

       对于一次向下调整,最坏的情况如例子所示,从根一路比较到叶子,比较的次数为完全二叉树的高度即时间复杂度为 O(logN)


2.删除堆顶元素

基本思想:

a.将最后一个元素移到堆顶:

        将堆中的最后一个元素(数组中的最后一个元素)移到堆顶位置(数组的第一个位置),原来的堆顶元素已被覆盖。此时,堆的结构是:除了根节点,子树都符合堆结构(很熟悉是不是,此时只需将堆顶元素向下调整即可~~)

b.将堆顶元素向下调整: 

对新放到堆顶的元素进行“向下调整”(Heapify Down)操作,确保堆的性质得以维护。

为什么是用最后一个元素?

       将最后一个元素移到堆顶并进行向下调整是删除堆顶元素的最佳策略,因为它既保持了完全二叉树的结构也只需将新的堆顶元素向下调整即可。效率最高。

时间复杂度分析: 

从堆顶开始向下调整,最坏情况可到O(log2)


 3.添加新元素

基本思想:

  1. 将新元素放在堆的最后一个节点后面(若数组位置不够需扩容)。

  2. 将新插入的元素进行向上调整,直到所有元素符合堆结构。

举例子:

个人理解:

向上调整跟向下调整其实差不多,只是观察的视角不一样。 在原本堆结构完善的情况下,从尾部进行向上调整,是很高效的,这一点与向下调整很像。一个元素的向下调整,其实就是另一个元素的向上调整。反之亦然。

时间复杂度分析:

        时间复杂度为:O(log⁡n)。因为从堆底开始调整,可能到达堆顶,最多需要经过堆的高度次操作,而堆的高度为 log⁡n。


 4.构建堆

A:自底向上构建

核心思想:

        构建堆的核心在于将数组看作一个完全二叉树,然后从最后一个非叶子节点开始,逐步对每个节点执行“向下调整”操作,以确保每个节点都符合堆的性质。

为什么从最后一个非叶子节点开始? 

1. 叶子节点无需调整

  • 叶子节点没有子节点,因此它们天然符合堆的性质,无需调整。所以,处理这些节点是没有意义的,应该直接跳过。

2. 如果从根节点开始调整?

  • 举个栗子:假设在建大根堆,而整个树只有一个最大值,而最大值刚好在该二叉树的最后一层!此时,若从根节点开始调整,第一次调整,无法保证最大值到达堆顶。且最大值,也还在堆底,没有靠近堆顶。

  • 并且后续如果要让处在最后一层的最大值达到堆顶位置,还会反复破坏先前梳理好的结构。效率太低,因此我们不采用从根节点开始调整。

3. 那么为什么是最后一个非叶子节点?

  • 个人理解:假设在建大根堆,从最后一个非叶子节点开始调整,等于自底向上调整,如果有大的元素,就可以让它及时浮上来,后面就可以逐级上升~早点回到它该属于的位置。

  • 如果我们从最后一个非叶子节点开始向上调整,这样可以确保在处理后续每一个节点时,它的子节点已经是符合堆性质的。效率高。

举个例子~~ 

时间复杂度分析: 

结论:构建堆的时间复杂度是 O(n),这里的 n 是数组中元素的数量。虽然每次调用 heapify 操作的最坏时间复杂度是 O(log⁡n),但由于堆的结构,在构建堆的过程中,并不是每个元素都需要向上调整到根节点,调整次数随深度递减。

推导:


 B:自顶向下构建

我们学了插入元素操作,那是不是可以往一个空树里,一个一个元素插入,构建出一个堆~~

基本思想:

        自上而下的构建方法,也称为“逐步插入法”,是从堆的根节点开始,一次插入一个元素,并在每次插入后进行向上调整(Heapify Up),直到所有元素都被插入为止。

时间复杂度分析: 

       结论:自上而下构建堆的时间复杂度为 O(nlog⁡n)。

       分析:在最坏情况下,每次插入元素时都需要进行 O(log⁡n)的向上调整,而总共有 n 个元素需要插入到堆中。


C:两种方式对比

      虽然自上而下构建堆的方法比较直观,但其时间复杂度较高尤其是对于大规模数据,自下而上的构建方法(时间复杂度 O(n))更为高效。 


三、尝试自己编程实现堆 

1.初始化堆和一些常用方法

public class MaxHeap {
    private int[] heap;
    private int size;//当前数量
    private int capacity;//当前最大容量

    // 初始化
    public MaxHeap(int capacity) {
        this.heap = new int[capacity];
        this.size = 0;
        this.capacity = capacity;
    }

    // 取索引
    // 父节点
    private int parent(int i) {
        return (i - 1) / 2;
    }

    // 找左子树
    private int leftChild(int i) {
        return 2 * i + 1;
    }

    // 找右子树
    private int rightChild(int i) {
        return 2 * i + 2;
    }

    // 交换堆内两个元素
    private void swap(int i, int j) {
        int temp = heap[i];
        heap[i] = heap[j];
        heap[j] = temp;
    }

2.向下调整

//向下调整
    private void heapifyDown(int i) {
        int largeIndex = i;
        int left = leftChild(i);
        int right = rightChild(i);

        //要找到左右节点中更大的元素
        if (left < size && heap[left] > heap[largeIndex]) {
            largeIndex = left;
        }
        if (right < size && heap[right] > heap[largeIndex]) {
            largeIndex = right;
        }

        if (largeIndex != i) {
            swap(i, largeIndex);
            heapifyDown(largeIndex);
        }
    }

3.向上调整

//向上调整
    private void heapifyUp(int i) {
        if (i <= 0) {
            return;
        }
        int parent = parent(i);
        if (heap[i] > heap[parent]) {
            swap(i, parent);
            heapifyUp(parent);
        }

注意:heapifyUpheapifyDown递归调用时,可能会导致堆栈溢出(特别是对于大的堆),可以考虑用循环代替递归。  

4.动态扩容

 //动态扩容
    private void enlargeCapacity() {
        //每次都是二倍扩容
        if (this.size == capacity) {
            int[] newHeap = new int[this.capacity * 2];
            System.arraycopy(heap, 0, newHeap, 0, size);
            this.heap = newHeap;
            this.capacity = this.capacity * 2;
        }
    }

        System.arraycopy 是 Java 中一个用于高效地复制数组元素的方法。它通过底层的本地代码进行操作,比普通的循环复制更高效。


public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
参数含义:

1.src:Object类型。源数组,即要从哪个数组中复制元素。
2.srcPos:int类型。源数组的起始位置,从该位置开始复制元素。
3.dest:Object类型。目标数组,即要将元素复制到哪个数组。
4.destPos:int类型。目标数组的起始位置,从该位置开始放置复制过来的元素。
5.length:int类型。要复制的元素个数,即从源数组中复制多少个元素到目标数组中

 

5.添加元素和删除元素

//添加元素
    private void add(int num) {
        if (size == capacity) {
            enlargeCapacity();
        }
        heap[size++] = num;
        heapifyUp(size - 1);
    }

//删除元素
    private int poll() {
        if(size==0){
            return -1;
        }
        int temp = heap[0];
        heap[0] = heap[size - 1];
        size--;
        heapifyDown(0);
        return temp;
    }

 四、Java中堆常用接口

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

1.构造方法 

2.常用方法

 


五、堆的应用

1.优先队列:

       堆通常用于实现优先队列,能够以高效的方式处理插入元素和取出最大或最小元素的操作。最大堆可以用于处理最大优先级队列,而最小堆则用于最小优先级队列。

2.排序算法:

       堆排序(Heap Sort)是一种基于堆的排序算法。它利用最大堆或最小堆的特性,每次取出堆顶元素(最大或最小值),然后重新调整堆。堆排序的时间复杂度为 O(nlogn)。

3.图算法:

        Dijkstra算法:用于计算单源最短路径。堆用作优先队列来快速获取当前未访问节点中距离最小的节点。
        Prim算法:用于计算最小生成树。堆用作优先队列来选择权重最小的边。

4.动态数据集合:

        在一些动态数据集合的应用中,如实时数据流处理,需要快速获取最大值或最小值,堆是理想的选择。通过堆可以高效维护一个动态的、部分排序的数据集合。

5.Top-K 问题:

        堆也常用于解决Top-K问题,比如从海量数据中找出前K大的元素。通常使用一个大小为K的小根堆来维护当前的前K个最大元素。

        使用小顶堆的原因是,它允许我们在每次遇到更大的元素时,迅速替换掉当前堆中最小的元素,这样最终堆中保留的就是前K大的元素。

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

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

相关文章

redis实战——go-redis的使用与redis基础数据类型的使用场景(二)

一.go-redis操作hash 常用命令&#xff1a; redisClient.HSet("map", "name", "jack") // 批量设置 redisClient.HMSet("map", map[string]interface{}{"a": "b", "c": "d", "e"…

基于springmvc实现文件上传

1.导入jar包 2.修改配置类 在springmvc.xml添加bean <!-- 配置文件上传处理器 --><bean id"multipartResolver" class"org.springframework.web.multipart.commons.CommonsMultipartResolver"><!-- 设置在内存中允许的最大文件大小&#x…

趣味算法------柠檬水摊

目录 题目概述&#xff1a; 解题思路&#xff1a; 具体代码&#xff1a; 总结&#xff1a; 题目概述&#xff1a; 在柠檬水摊上&#xff0c;每个柠檬水售价 5 元。客户正在排队向您购买&#xff0c;并且一次订购一份柠檬水。 每位顾客只会购买一份柠檬水&#xff0c;并支付 5…

【python】火灾检测图像处理方法设计(源码+论文)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

深入探讨Java多线程

我的主页&#xff1a;2的n次方_ 1. 多线程的概念 多线程是指在同一个程序中同时执行多个线程的技术。线程是操作系统能够独立调度和执行的最小单位。在Java中&#xff0c;线程由Thread类来表示&#xff0c;所有的线程都是通过这个类或其子类来创建和控制的。通过合理的多线…

解决ERROR: No matching distribution found for imp报错问题

一、问题描述 当我们使用Python3.4及其以上版本运行Python项目时&#xff0c;提示【ModuleNotFoundError: No module named imp】&#xff0c;但是我们使用【pip install imp】命令安装imp时却提示如下错误信息&#xff1a; ERROR: Could not find a version that satisfies t…

深入理解Java代理模式:从静态到动态的实现与应用

1、引言 在Java编程中&#xff0c;代理模式是一种常见的设计模式&#xff0c;用于在不修改原始代码的情况下&#xff0c;为对象添加额外的功能。代理模式有两种主要类型&#xff1a;静态代理和动态代理。本文将全面探讨这两种代理模式&#xff0c;包括它们的基本概念、实现方式…

增材制造(3D打印):为何备受制造业瞩目?

在科技浪潮的推动下&#xff0c;增材制造——即3D打印技术&#xff0c;正逐步成为制造业领域的璀璨新星&#xff0c;吸引了航空航天、汽车、家电、电子等众多行业的目光。那么&#xff0c;是什么让3D打印技术如此引人注目并广泛应用于制造领域&#xff1f;其背后的核心优势又是…

VSCODE SSH连接失败

前提&#xff1a;以前连接得好好的 突然有一天就连接不上了 打开C盘下的known_hosts文件删除如下内容&#xff0c;重新登陆即可

天正如何保存低版本

打开天正cad的界面。左边找到文件布图这个菜单&#xff0c;点击进入找到图形导出这个子菜单&#xff0c;之后会出现下面这一界面。 第2步 可以看到保存类型&#xff0c;一进去是天正3文件的&#xff0c;这时候你要点开下拉选择天正6文件&#xff0c;其它可以不用修o改&#x…

Keepalived和Nginx一起在Centos7上实现Nginx高可用设计

方案概览 如需详细信息可点击下列链接进行视频观看 B站 7分钟弄懂啥是高可用基石-VIP从零开始实操VIP 抖音 7分钟弄懂啥是高可用基石-VIP从零开始实操VIP Centos7 yum更新 安装阿里yum源 wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Cent…

TCP/UDP的对比,粘包分包抓包,http协议

服务器端&#xff1a; 一、loop 127.0.0.1本地回环测试地址 二、tcp特点 面向连接、可靠传输、字节流 粘包问题&#xff1a;tcp流式套接字&#xff0c;数据与数据之间没有套接字&#xff0c;导致可能多次的数据粘到一起 解决方法&#xff1a;&#xff08;1&#xff09;规…

Linux数据相关第1个服务_备份服务rsync

1、备份服务概述 备份服务&#xff1a;需要使用到脚本&#xff0c;打包备份&#xff0c;定时任务 备份服务&#xff1a;rsyncd 服务&#xff0c;不同主机之间数据传输 特点: rsync是个服务也是命令使用方便&#xff0c;具有多种模式传输数据的时候是增量传输 增量与全量&am…

Nginx: 配置项之http模块connection和request的用法以及limit_conn和limit_req模块

connection和request connection 就是一个连接, TCP连接 客户端和服务器想要进行通信的话&#xff0c;有很多种方式比如说, TCP的形式或者是UDP形式的通常很多应用都是建立在这个TCP之上的所以, 客户端和服务器通信&#xff0c;使用了TCP协议的话&#xff0c;必然涉及建立TCP连…

一分钟告诉你毕业季大学都在用在线版招生简章是如何制作?

毕业季临近&#xff0c;各大高校纷纷进入招生宣传的关键时期。在数字化时代背景下&#xff0c;在线版招生简章成为了高校之间竞争的焦点。一分钟带你了解&#xff0c;这些吸引眼球的在线版招生简章是如何制作出来的。 1. 准备好制作工具&#xff1a;FLBOOK在线制作电子杂志平台…

【论文分享】Graviton: Trusted Execution Environments on GPUs 2018’OSDI

目录 AbstractIntroductioncontributions BackgroundGPUSoftware stackHardwareContext and channel managementCommand submissionProgramming modelInitializationMemory allocationHost-GPU transfersKernel dispatch Sharing Intel SGX Threat ModelOverviewGraviton Archi…

World of Warcraft [CLASSIC] Engineering 335-420

World of Warcraft [CLASSIC] Engineering 工程学冲技能点 335 - 420 [冰霜冲击雷管] 335-345 [冰霜手雷] 346-358 这部分知道可以不看了 在地狱火半岛&#xff0c;萨尔玛&#xff0c;找70级工程学大师学习新的技能&#xff0c;用来充技能都不划算 回【达拉然】找80级工程…

【电子数据取证】提升案件分析准确性的去重技术

前言 紧随《AES解密侵犯隐私案件数据》一文的讨论&#xff0c;本文将深入探讨数据解密后的处理工作。解密只是数据恢复的第一步&#xff0c;确保数据的准确性和分析的有效性同样重要。本文将重点介绍数据去重技术&#xff0c;阐述在解密数据后如何细致地进行去重处理&#xff…

wx.choosemedia 无反应 不生效 不弹出图片视频

调整开发者工具基础版本&#xff1a;打开微信开发者工具 > 右上方点击详情 > 本地设置 >调试基础库&#xff08;更换版本&#xff09; 这里我试了几个&#xff0c;发现 3 开头的版本都不行。。最后选了 2.30.4版本&#xff0c;官方文档给的答案是 2.10.0 开始支持 。…

特殊管道资源采购

管道资源物料是从管道&#xff08;如输油管&#xff09;或其它线管&#xff08;如输电线&#xff09;中直接进入生产流程的物料。与寄售物料不同的是&#xff0c;管道资源物料不必一定经过“采购”获得。管道库存不是在仓库中实际可用的库存。管道中的物料始终可用&#xff0c;…