10、数据结构与算法——堆

news2025/1/31 8:09:16

一、什么是堆

  堆是一种特殊的树形数据结构,通常实现为完全二叉树满二叉树。堆又分为两种类型最大堆(Max Heap)最小堆(Min Heap)

1.1、什么是二叉树

  二叉树是一种数据结构,它是由n(n≥0)个节点的有限集合构成,每个节点最多有两个子节点,通常分别称为左子节点和右子节点。

  • n:表示二叉树中的节点总数,它可以是一个任意非负整数。

  • n≥0:这意味着二叉树可以包含任意数量的节点,包括零个。即:
    当n=0时,表示这是一棵空二叉树,它不包含任何节点。
    当n>0时,表示二叉树至少包含一个节点,并且根据定义,这个节点可能还有0个、1个或2个子节点,依此类推,形成一个节点的有限集合。

  • 二叉树如下图:每个节点最多有两个子节点,通常分别称为左子节点和右子节点。
    在这里插入图片描述

1.2、什么是完全二叉树

  除了最后一层外,其他层都是完全填满的,最后一层的节点都尽可能地靠左排列,允许右侧存在空节点,但左侧不允许出现空位(除非右边的所有位置也都为空)。
在这里插入图片描述

1.3、什么是满二叉树

  所有层都被完全填满,且所有叶子节点都在最底层,没有空缺的位置。
在这里插入图片描述

1.4、什么是最大堆(或大顶堆)

  对于任意节点,其父节点的值大于等于(≥)它的两个子节点的值。换句话说,堆顶元素(根节点)始终是整个堆中最大的元素。
在这里插入图片描述

1.5、什么是最小堆(或小顶堆)

  对于任意节点i,其父节点的值小于等于(≤)它的两个子节点的值。因此,在最小堆中,堆顶元素始终是最小的。
在这里插入图片描述

二、堆的应用场景

2.1、优先队列(Priority Queue)

  堆可以高效地实现优先队列,其中元素按照它们的优先级(或值)进行排序。在最大堆中,优先级最高的元素位于根节点,每次删除时总是取出优先级最高的元素;在最小堆中,则是取出优先级最低的元素。

2.2、堆排序(Heapsort)

  堆排序是一种基于比较的排序算法,它首先将待排序的序列构造成一个大顶堆或小顶堆,然后不断交换堆顶元素与末尾元素并调整堆结构,直到整个序列有序。

2.3、内存管理(Memory Management)

  在操作系统中,内存分配器可能会使用堆数据结构来管理内存碎片和动态分配内存请求。尽管这里的“堆”通常指的是内存区域而不是数据结构,但在某些情况下,其分配策略会借鉴堆数据结构的特性。

2.4、图算法

  Dijkstra算法、Prim算法和Kruskal算法等解决最短路径问题时,利用优先队列(通过堆实现)快速访问当前已知的最小距离节点。

2.5、事件调度

  在实时系统和事件驱动的应用程序中,堆用于存储具有优先级的任务或事件,确保高优先级任务优先得到处理。

2.6、Top K问题

  找出一组数据中的前K个最大或最小元素,可以通过维护大小为K的最小堆或最大堆快速得到结果。

2.7、总结

  总之,堆提供了一种高效的数据结构来支持对大量数据进行快速查找、插入和删除操作,尤其当需要保持数据的某种顺序性或优先级时。

三、堆的特征

3.1、堆数据的存储方式可以用数组来实现。

如下图
在这里插入图片描述

3.2、堆中父子节点的关系

3.2.1、如何获任意节点 i 的两个子节点索引位置

a、获取左边子元素
  • 公式

左子节点索引= (2 * i 的索引位置) + 1

  • 案例

如上图:17的索引位置是3 ,17的左子节点索引位置是7
7 = (3 * 2) + 1

b、获取右边子元素
  • 公式

右子节点索引= (2 * i 的索引位置) + 2

  • 案例

如上图:17的索引位置是3 ,17的右子节点索引位置是8
8 = (3 * 2) + 2

3.2.2、如何获任意节点 j 的父节点索引位置

  • 公式

父节点索引 =( j的索引位置 - 1)/ 2 (除数向下取整)

  • 案例

案例1: 如上图,17的索引位置是3 ,17的父节点索引位置是1
1 = ( 3 - 1 ) / 2

案例2: 如上图,1的索引位置是6 ,1的父节点索引位置是2
1= ( 6 - 1 ) / 2   // 除数向下取整.

3.2.3、如何获堆中最后一个非叶子节点索引

  • 公式

最后一个非叶子节点的索引 = (堆中元素个数/2)-1 //除数向下取整

  • 案例

如上图;堆中元素共9个。
3 = ( 9 / 2) - 1  // 除数向下取整 ,索引位置3就是就是最后一个非叶子节点-》17

3.3、堆的常用方法(以大顶堆为例)

3.3.1、堆的初始化

为堆分配一个数组,并设定初始容量。

/**
 * 大顶堆
 */
public class MaxHeap {

    /**
     * 用数组存储堆元素
     */
    private int[] array;

    /**
     * 堆中元素个数
     */
    private int size;

    /**
     * @param capacity 堆的容量
     */
    public MaxHeap(int capacity) {
        this.array = new int[capacity];
        this.size = 0;
    }
}

3.3.2、建立堆

3.3.2.1、建立堆思路

  传入一组随意的数组数据,然后从最后一个非叶子节点开始,依次往上对每个节点执行元素下潜操作,最终得到一个合法的堆结构。

3.3.2.2、具体代码
package heap;

/**
 * 大顶堆
 */
public class MaxHeap {

    /**
     * 用数组存储堆元素
     */
    private int[] array;

    /**
     * 堆中元素个数
     */
    private int size;

    /**
     * @param capacity 堆的容量
     */
    public MaxHeap(int capacity) {
        this.array = new int[capacity];
        this.size = 0;
    }

    /**
     * 建立堆思路:对于一组无序的数据,可以先将其放入一个数组中,然后从最后一个非叶子节点开始,
     * 依次往上对每个节点执行元素下潜操作,最终得到一个合法的堆结构。
     * <p>
     * 步骤:
     * 1、传入一个无序的数组数据。
     * 2、如何找到最后一个非叶子节点:公式为:(size/2)-1 就是最后一个非叶子节点索引位置。
     * 3、然后依次往上,对每个节点执行下潜。
     *
     * @param array 需要创建成堆的数组
     */
    public MaxHeap(int[] array) {
        this.array = array;
        this.size = array.length;
        //找到第一个叶子节点
        for (int i = (size / 2) - 1; i >= 0; i--) {
            down(i);
        }
    }

    /**
     * 元素下潜方法
     * 下潜原理:与左右两个子元素进行比较,如果最大的那个子元素大于当前下潜元素。
     * 那么与该子元素进行索引替换。注意事项:左右子元素的索引不能超过数组长度的范围(必须小于size)
     *
     * @param diveIndex 下潜元素索引
     */
    private void down(int diveIndex) {
        int leftIndex = (diveIndex * 2) + 1;  //获取左边子元素索引
        int rightIndex = leftIndex + 1;//获取右边子元素索引;等同于(diveIndex * 2) + 2
        int max = diveIndex;  //第一个临时变量记录最大那个子元素索引。默认为-1;

        //如果左边子元素大于下潜元素,max记录左边子元素索引
        if (leftIndex < size && array[leftIndex] > array[max]) {
            max = leftIndex;
        }
        //如果右边子元素大于下潜元素,max记录右边子元素索引
        if (rightIndex < size && array[rightIndex] > array[max]) {
            max = rightIndex;
        }

        //不等于-1 表示有比下潜元素大的子元素
        if (max != diveIndex) {
            //交换元素位置
            swap(diveIndex, max);
            down(max);//然后继续下潜
        }
    }

    /**
     * 元素索引交换方法。
     */
    private void swap(int i, int j) {
        int t = array[i];
        array[i] = array[j];
        array[j] = t;
    }
}
3.3.2.3、测试

代码

    public static void main(String[] args) {
        int[] array = {1, 2, 3, 4, 5, 6, 7};
        MaxHeap maxHeap=new MaxHeap(array);
        System.out.println(Arrays.toString(maxHeap.array));
    }

测试前的堆
在这里插入图片描述
输出结果

[7, 5, 6, 4, 2, 1, 3]

在这里插入图片描述

3.3.3、获取堆顶元素

3.3.3.1、实现思路

  在大顶堆中,最大元素总是位于根节点。所以。首先移除根节点做一个临时存储,用于返回结果,然后将根节点元素与最后一个元素进行索引调整,这时根节点到了数组最后面。直接将最后一个值赋值为空,最后然后把新的根节点向下调整(称为“下潜”或“heapify-down”),使其满足堆性质。

3.3.3.2、代码
    /**
     * 获取堆顶元素
     *
     * @return
     */
    public int poll() {
        int data = array[0];//临时存储堆顶元素
        swap(0, size - 1);//将堆顶元素和最后一个元素跟换索引位置
        array[size - 1] = -1;//表示赋值为空(-1),
        size--;//元素个数-1;
        down(0);//然后把新的根节点向下调整(称为“下潜”或“heapify-down”),使其满足堆性质。
        return data;
    }
3.3.3.3、测试
    public static void main(String[] args) {
        int[] array = {1, 2, 3, 4, 5, 6, 7};

        MaxHeap maxHeap = new MaxHeap(array);
        int peek = maxHeap.poll();
        System.out.println("取出的值为:" + peek);
        System.out.println(Arrays.toString(maxHeap.array));
    }

在这里插入图片描述

3.3.4、获取指定索引位置元素(和获取堆顶元素逻辑类似)

    /**
     * 获取指定索引元素
     *
     * @return
     */
    public int poll(int index) {
        int data = array[index];//临时存储堆顶元素
        swap(index, size - 1);//将堆顶元素和最后一个元素跟换索引位置
        array[size - 1] = -1;//表示赋值为空(-1),
        size--;//元素个数-1;
        down(index);//然后把新的index节点向下调整(称为“下潜”或“heapify-down”),使其满足堆性质。
        return data;
    }

3.3.5、替换堆顶元素

    /**
     * 替换堆顶元素
     *
     * @param replaced 新元素
     */
    public void replace(int replaced) {
        array[0] = replaced;
        down(0); // 然后把新的根节点向下调整(称为“下潜”或“heapify-down”),使其满足堆性质。
    }

3.3.6、堆添加元素

3.3.6.1、实现思路

  当向堆中插入一个新元素时,通常会将新元素添加到堆的末尾(数组的下一个空闲位置),然后通过自下而上的调整过程(称为“上浮”或“heapify-up”)来确保新的堆仍满足堆性质。

3.3.6.2、代码
    /**
     * 堆的尾部添加元素
     *
     * @param offered 新元素
     * @return 是否添加成功
     */
    public boolean offer(int offered) {
        //数组是否已满。
        if (size == array.length) {
            return false;
        }
        up(offered);//元素上浮
        size++;//元素个数+1
        return true;
    }

    // 将 offered 元素上浮: 直至 offered 小于父元素或到堆顶
    private void up(int offered) {
        int child = size;//需要上浮的所以位置
        while (child > 0) {//如果上浮位置大于0表示还可以上浮
            int parent = (child - 1) / 2;//获取上浮索引的父索引位置

            //如果新元素大于父元素,将元素赋值到新元素位置
            if (offered > array[parent]) {
                array[child] = array[parent];
            } else {
                break;
            }
            child = parent;//将父元素索引设置为需要上符的索引
        }
        array[child] = offered;//上浮结束。那么将最终的上浮位置设置为新元素。
    }

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

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

相关文章

Python 计算两个时间之间的耗时

博主按照自己的需求写的一个可以计算程序耗时的函数&#xff0c;各位有需要的自行改写哈 实现的大致功能 功能1 记录开始时间&#xff0c;返回一个时间字典。数据包括&#xff1a;开始时间的日期格式、文本格式 功能2 记录结束时间和耗时&#xff0c;返回一个时间字典。数据包…

Web3智能合约:重新定义商业合作的未来

随着区块链技术的飞速发展&#xff0c;Web3时代正逐渐到来&#xff0c;而其中的智能合约成为推动商业合作变革的关键力量。本文将深入探讨Web3智能合约的概念、特点以及对商业合作未来的巨大影响。 什么是Web3智能合约&#xff1f; 智能合约是一种以代码形式编写、自动执行合同…

Oracle 面试题 | 05.精选Oracle高频面试题

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

Flask 入门4:Flask 模板

1. 前言 Flask 拥有丰富的扩展方法&#xff0c;且都有统一的特点&#xff1a;简单和即学即用。当我们要实现某个功能之前&#xff0c;可以提前去搜一搜这个功能包是否已经存在&#xff0c;这样也能帮助我剩下很多时间。那么要去哪里找到这些扩展包呢&#xff0c;这里推荐两个方…

Android:RecyclerView跨行跨列的LayoutManager:Spannedgridlayoutmanager

前言&#xff1a; RecyclerView可以使用GridLayoutManager实现跨行&#xff0c;但是不能跨列&#xff1b;瀑布流布局可以跨列但是又不能跨行。原生自带的各个LayoutManager中并没有可以又跨行又能跨列的。网上搜寻了一番&#xff0c;找到了一个亲测可行好用的三方库&#xff1…

微信小程序可以一个人开发么?

随着移动互联网的飞速发展&#xff0c;微信小程序已经成为了人们日常生活中不可或缺的一部分。它以其便捷性和高效性赢得了广大用户的青睐。对于许多有志于进入小程序领域的开发者来说&#xff0c;一个常见的问题是&#xff1a;微信小程序可以一个人开发吗&#xff1f;答案是肯…

2024 Flutter 重大更新,Dart 宏(Macros)编程开始支持,JSON 序列化有救

说起宏编程可能大家并不陌生&#xff0c;但是这对于 Flutter 和 Dart 开发者来说它一直是一个「遗憾」&#xff0c;这个「遗憾」体现在编辑过程的代码修改支持上&#xff0c;其中最典型的莫过于 Dart 的 JSON 序列化。 举个例子&#xff0c;目前 Dart 语言的 JSON 序列化高度依…

【笔记】React-Native Navigation页面导航

/** * 须知&#xff1a;a bare React Native Project(not an Expo managed project) * * React官方文档&#xff1a;https://reactnative.dev/docs/navigation * * 当前文档基于6.x * React Navigation文档&#xff1a;https://reactnavigation.org/docs/getting-started * …

C++ Webserver从零开始:基础知识(七)——多进程编程

前言 在学习操作系统时&#xff0c;我们知道现代计算机往往都是多进程多线程的&#xff0c;多进程和多线程技术能大大提高了CPU的利用率&#xff0c;因此在web服务器的设计中&#xff0c;不可避免地要涉及到多进程多线程技术。 这一章将简要讲解web服务器中的多进程编程&#x…

AI与数字孪生

源自&#xff1a;译文 “人工智能技术与咨询”发布 AI和数字孪生 预测分析&#xff1a;网络安全水晶球 面对不断变化的威胁&#xff0c;提供自适应安全防护 自然语言处理&#xff1a;解码威胁语言 先进技术&#xff1a;人工智能作为数字孪生安全的基础 道德考量 …

关于v8垃圾回收机制以及与其相关联的知识点--还没整理版本

对于值类型b来说&#xff0c;就直接释放了其占用的内存&#xff0c;对于引用类型obj来说&#xff0c;销毁的只是变量obj对堆内存地址 1001 的引用&#xff0c;obj的值 { c: 3 } 依然存在于堆内存中。那么堆内存中的变量如何进行回收呢&#xff1f; V8的垃圾回收策略主要是基于…

git将项目的某次签入遴选(Cherry-Pick)另一个项目

需求&#xff1a;将项目Product&#xff0c;分支feature/platform&#xff0c;签入959294ce6b75ee48c5cb22c46d7398654628a896&#xff0c;遴选到项目BRP&#xff0c;分支dev 第一步&#xff1a;使用原签入生成patch文件&#xff08;git format-patch -1 <commit_hash>&a…

西部数据DX4000数据恢复案例

西部数据DX4000是一款NAS&#xff08;网络附加存储&#xff09;产品&#xff0c;它集成了存储、备份、共享和远程访问企业数据等多种功能。然而&#xff0c;和任何电子设备存在一样通病&#xff0c;DX4000也可能会出现故障&#xff0c;导致数据无法访问。下面是一个客户的西部数…

Python OpenCV实现图片像素区域缩放

Python OpenCV实现图片像素区域缩放 前言项目安装OpenCV和Pillow思路代码编写 前言 遇到一个要将大量图片缩放成统一规格的难题&#xff0c;并且这些图片周围还有很多空白像素&#xff0c;所以用Python实现一下。 项目 安装OpenCV和Pillow pip install opencv-python pip …

基于YOLOv8的水下生物检测,多种优化方法---DCNv4结合SPPF,效果秒杀DCNv3,涨点两个点(四)

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文主要内容:详细介绍了水下生物检测整个过程&#xff0c;从数据集到训练模型到结果可视化分析&#xff0c;以及如何优化提升检测性能。 &#x1f4a1;&#x1f4a1;&#x1f4a1;加入DCNv4_SPPF mAP0.5由原始的0.522提升至0.543 1.水…

7.3、向量空间的简要回顾

7.3、向量空间的简要回顾 ​ 在开始讨论格之前&#xff0c;我们先提醒读者注意线性代数中的一些重要定义和思想。向量空间的定义可以非常宽泛&#xff0c;但就本章而言&#xff0c;我们只需考虑对于某个正整数 m&#xff0c;包含在 R m R^{m} Rm中的向量空间即可。 ​ 我们从…

[职场] 阐述演员这个职业 #微信#学习方法#媒体

阐述演员这个职业 演员这个职业是一种极具挑战性和魅力的职业&#xff0c;它让人们有机会通过表演来展现自己&#xff0c;将自身的才华和潜力发挥到极致。 1. 演员需要具备扎实的专业素养&#xff0c;包括表演技巧、语言技巧、心理技巧等。演员需要深入理解角色&#xff0c;从…

大数据 - Spark系列《一》- 分区 partition数目设置详解

目录 &#x1f436;3.2.1 分区过程 &#x1f436;3.2.2 SplitSize计算和分区个数计算 &#x1f436;3.2.3 Partition的数目设置 1. &#x1f959;对于数据读入阶段&#xff0c;输入文件被划分为多少个InputSplit就会需要多少初始task. 2. &#x1f959;对于转换算子产生的…

【Bugs】Jmeter报错:NoSuchMethodError: org.apache.jmeter.samplers.

报错情况 Jmeter版本&#xff1a;5.4.3 报错场景&#xff1a;在线程组中添加了jpgc - PerfMon Metrics Collector性能监控组件后出现报错。 Jmeter中无法运行测试&#xff0c;cmd命令行中出现以下报错。 cmd报错详细内容&#xff1a; Uncaught Exception java.lang.NoSuchMe…

如何系统的自学Python?通义千问、讯飞星火、文心一言及ChatGPT的回答

如何系统的自学Python&#xff1f;来看看通义千问、讯飞星火、文心一言及ChatGPT的回答. 第一个是马老师的通义千问 系统地自学Python是一个循序渐进的过程&#xff0c;从基础语法到实践项目&#xff0c;再到专业领域的深入学习。下面是一个详细的步骤指南&#xff1a; 了解Py…