数据结构与算法-堆

news2024/11/19 6:17:58

什么堆
堆是一种特殊的数据结构,是最高效的优先级队列。可以被看作是一颗完全的二叉树表达的数组对象。

堆的定义
1、必须是一颗完全二叉树
完全二叉树就是子节点必须从左到右都是完整的,没有缺失。
在这里插入图片描述

2、用数组表示看作完全二叉树的堆
假设:当前节点索引为 index
则:
左子节点索引 = 2index+1
右子节点索引 = 2index+2
父节点索引 = (index-1)/2

3、每个节点元素大于或等于其所有子节点大小

堆的时间复杂度
1、n个节点的堆,其根节点为深度0,则第i深度有2^i个元素,构建堆的时间复杂度为O(n)
2、n个节点的深度为logn,插入和删除元素后需要堆元素进行上下交换,故时间复杂度都为O(logn)

优先级队列 VS 堆
优先级队列删除操作时间复杂度为 O(1),插入操作必须保证顺序时间复杂度为 O(n)
由于n个节点的树高度为 logn,在新增和删除数据需要上下移动元素,故时间复杂度都为 O(logn)

小试牛刀
本次我们讨论新增删除最大堆元素,其他操作较为简单不做讨论
1、提供看作完全二叉树数据结构的工具类

工具类提供增加和删除数据方法

/**
 * 最大堆
 * @author senfel
 * @version 1.0
 * @date 2022/12/28 8:59
 */
public class HeapDemo {

    /**
     * 数组
     */
    private Node[] array;
    /**
     * 下一次插入的索引地址
     */
    private int nextIndex;
    /**
     * 最大容量
     */
    private int maxSize;

    public HeapDemo(int maxSize) {
        this.maxSize = maxSize;
        //默认起始位置作为待插入位置
        this.nextIndex=0;
        this.array = new Node[maxSize];
    }



    /**
     * 新增数据
     * 默认插入最末尾节点,然后往上移动找到适合的位置
     * @param data
     * @author senfel
     * @date 2022/12/28 9:05
     * @return java.lang.Boolean
     */
    public Boolean insert(int data){
        if(nextIndex > maxSize-1){
            //数据组已满
            return false;
        }
        Node node = new Node(data);
        //默认插入到堆末尾
        array[nextIndex] = node;
        //为满足最大堆,数据往上移动找到具体的位置
        elementUp(nextIndex);
        //设置下一个待插入节点
        nextIndex++;
        return true;
    }


    /**
     * 移除根节点数据
     * @author senfel
     * @date 2022/12/28 9:45
     * @return java.lang.Boolean
     */
    public Boolean remove(){
        if(nextIndex == 0){
            return false;
        }
        //将根节点数据重置为末尾节点元素,达到删除根节点效果
        array[0] = array[--nextIndex];
        //被重置的根节点需要往下移动调整位置以满足最大堆
        elementDowm(0);
        //由于末尾元素被移动需要重置为空
        array[nextIndex] = null;
        return true;
    }





    /**
     * 往上移动元素满足最大堆
     * @param index
     * @author senfel
     * @date 2022/12/28 9:08
     * @return void
     */
    private void elementUp(int index) {
        //缓存当前节点数据
        Node temp = array[index];
        //获取当前节点父节点索引
        int parentIndex = (index-1)/2;
        //循环到根节点 并且 父节点不小于子节点 结束
        while (index > 0 && array[index].getData() > array[parentIndex].getData()){
            //父节点移动到当前节点 将当前节点重置为父节点数据
            array[index] = array[parentIndex];
            //将当前节点重置为父节点位置
            index = parentIndex;
            //继续往上找到当前节点父节点
            parentIndex = (index -1)/2;
        }
        //当前节点数据写入
        array[index] = temp;
    }


    /**
     * 往下移动元素满足最大堆
     * @param index
     * @author senfel
     * @date 2022/12/28 9:49
     * @return void
     */
    private void elementDowm(int index) {
        //根节点数据缓存
        Node temp = array[index];
        //找到最后一个节点结束循环
        //只要父节点索引 大于 最后一个父节点索引即可
        while (index <= (nextIndex -1)/2){
            //获取当前节点左右子节点索引
            int leftIndex = 2*index +1;
            int rightIndex = 2*index +2;
            int currentIndex = 0;
            if(leftIndex < nextIndex && array[leftIndex].getData() > array[rightIndex].getData()){
                //左节点数据大于右节点
                currentIndex = leftIndex;
            }else{
                currentIndex = rightIndex;
            }
            if(temp.getData() >= array[currentIndex].getData()){
                //如果当前节点大于等于子节点 则停止移动
                break;
            }
            //子节点移动到当前节点 将当前节点重置为子节点数据
            array[index] = array[currentIndex];
            index = currentIndex;
        }
        //当前节点赋值
        array[index] = temp;
    }


    @Data
    private class Node{
        /**
         * 数据
         */
        private int data;

        public Node(int data) {
            this.data = data;
        }
    }
}

2、新增测试方法
测试方法提供新增、删除元素

public static void main(String[] args) {
        HeapDemo heap = new HeapDemo(10);
        heap.insert(100);
        heap.insert(90);
        heap.insert(98);
        heap.insert(80);
        heap.insert(88);
        heap.insert(91);
        heap.insert(97);
        heap.insert(78);
        heap.insert(79);
        heap.insert(87);
        System.err.println(Arrays.toString(heap.array));
        heap.remove();
        System.err.println(Arrays.toString(heap.array));
        heap.remove();
        System.err.println(Arrays.toString(heap.array));
}

3、查看测试结果并提供图示
3.1 向堆新增10个元素
算法原理为:
每次新增的元素放置在堆底部,然后依次比较父节点元素大小,如果父节点小于子节点则需要移动位置以满足最大堆。
数据结果为:

[HeapDemo.Node(data=100), HeapDemo.Node(data=90), HeapDemo.Node(data=98), HeapDemo.Node(data=80), HeapDemo.Node(data=88), HeapDemo.Node(data=91), HeapDemo.Node(data=97), HeapDemo.Node(data=78), HeapDemo.Node(data=79), HeapDemo.Node(data=87)]

完全二叉树模拟图:
在这里插入图片描述

3.2 向堆删除顶部元素100
算法原理为:
每次删除将最底部元素赋值给顶部元素,达到删除顶部元素效果。然后依次比较子节点元素大小,如果子节点大于父节点则需要移动元素以满足最大堆,最后重置底部元素为空即可。
数据结果为:

[HeapDemo.Node(data=98), HeapDemo.Node(data=90), HeapDemo.Node(data=97), HeapDemo.Node(data=80), HeapDemo.Node(data=88), HeapDemo.Node(data=91), HeapDemo.Node(data=87), HeapDemo.Node(data=78), HeapDemo.Node(data=79), null]

完全二叉树模拟图:
在这里插入图片描述

3.3 向堆删除顶部元素98
算法原理为:
每次删除将最底部元素赋值给顶部元素,达到删除顶部元素效果。然后依次比较子节点元素大小,如果子节点大于父节点则需要移动元素以满足最大堆,最后重置底部元素为空即可。
数据结果为:

[HeapDemo.Node(data=97), HeapDemo.Node(data=90), HeapDemo.Node(data=91), HeapDemo.Node(data=80), HeapDemo.Node(data=88), HeapDemo.Node(data=79), HeapDemo.Node(data=87), HeapDemo.Node(data=78), null, null]

完全二叉树模拟图:
在这里插入图片描述

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

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

相关文章

基于MYSQL的互联网药品交易系统数据库设计项目实战

说明&#xff1a;这是一个数据库课程设计实战项目&#xff08;附带代码文档视频讲解&#xff09;&#xff0c;如需代码文档视频讲解可以直接到文章最后获取。 项目背景 疫情期间实体经济收到打击&#xff0c;实体药店经营困难&#xff0c;医院也面临着疫情患者的压力运转着&…

window安装Docker Desktop for Windows(基于WSL2)

目录一 什么是Ducker二 安装Ducker1 window for Linux子系统环境搭建1.1 打开系统虚拟机平台和WSL(适用于Linux的window子系统)1.2安装WSL21.3为什么用wsl2&#xff0c;不用wsl11.4 下载集成支持linux内核的wsl21.5 设置WSL默认版本二、安装Ubuntu20.04LTS1 打开Microsoft Stor…

解析某音短视频X-Bogus

文章目录写在前面目标正向梳理主要加密函数主要算法解析逆向梳理结论测试进阶写在后面写在前面 首先推荐一篇资料&#xff1a; 某音短视频 X-Bogus 逆向分析&#xff0c;JSVMP 纯算法还原 这篇文章介绍了通过插桩日志抠算法的过程&#xff0c;很有参考价值。 文章已经实现的部…

什么是外汇跟单?外汇MT4自动跟单系统靠谱吗?

对于很多刚刚进入外汇市场的新手投资者而言&#xff0c;必须踏踏实实学习基础知识和交易技巧&#xff0c;才能在外汇市场获取盈利&#xff0c;因为在没有任何专业知识的情况下&#xff0c;想要在外汇市场盈利是一件非常艰难的事情。而一些脑洞大开的外汇新手投资者却在思考&…

第7章 分页之模型分页(物理分页)

模型分页主要通过前端页面中的分页控件&#xff0c;向后端指定路由(控制器行为方法)&#xff0c;传递相等的参数&#xff0c;然后间接的通过后端程序从数据库指定表中获取一些指定行的数据&#xff0c;然后把这些数据在前端页面渲染显示出来。模型分页的主要用于前端页面与后端…

Java开发 - 数据库索引的数据结构

目录 前言 题外话 什么是索引 索引的使用场景 索引的失效瞬间 索引的数据结构 Tree BTree BTree 索引提高查询效率的原因 索引的分类 innodb的特点 聚簇索引 非聚簇索引 索引操作 创建索引 查询索引 删除索引 外键 结语 前言 上一章中讲解了慢sql优化的方…

快速诊断I/O性能问题

背景客户反馈最近一段时间数据库运行缓慢&#xff0c;磁盘的压力很大&#xff0c;现在有两种不同的分析结论&#xff0c;存储设备性能下降和数据库压力变大&#xff0c;请我们进行系统的分析&#xff0c;给一个结论 现象 登录SQL专家云&#xff0c;进入性能指标页面&#xff0c…

跨境电商小白:一件代发是什么?为什么要选择Starday一件代发?

近两年经济形势比较复杂多变&#xff0c;许多人面临就业难、职场改革、行业转型等困境&#xff0c;而跨境电商行业在此形势下依靠着国家优惠政策以及中国高质量产品在海外市场获得越来越多的份额。随着跨境电商行业发展规模的扩大&#xff0c;越来越多人选择加入到跨境电商赛道…

【实时数仓】省份交易额统计接口、新老访客对比接口、访客分时统计接口

文章目录一 省份交易额统计接口1 Sugar配置&#xff08;1&#xff09;图表配置&#xff08;2&#xff09;接口地址&#xff08;3&#xff09;数据格式&#xff08;4&#xff09;执行SQL2 数据接口实现&#xff08;1&#xff09;创建地区交易额统计实体类ProvinceStats&#xff…

Kafka基础_2

Kafka系列 注&#xff1a;大家觉得博客好的话&#xff0c;别忘了点赞收藏呀&#xff0c;本人每周都会更新关于人工智能和大数据相关的内容&#xff0c;内容多为原创&#xff0c;Python Java Scala SQL 代码&#xff0c;CV NLP 推荐系统等&#xff0c;Spark Flink Kafka Hbase …

18-RocketMQ源码解读

NameServer启动 1、功能回顾 NameServer的核心作用 一是维护Broker的服务地址并进行及时的更新。 二是给Producer和Consumer提供服务获取Broker列表。 2、启动流程-源码重点 整个NameServer的核心就是一个NamesrvController对象。这个controller对象就跟java Web开发中的Contr…

3D可视化大屏是如何实现的?

3D可视化是指拥有3D效果的数据可视化&#xff0c;对于所要展示的数据可视化内容还原出真实场景&#xff0c;并实时接入数据&#xff0c;在面对复杂操作时灵活应对&#xff0c;使得整个场景在大屏上的展示更具立体、更具科技感、更具易用性。 物联网时代&#xff0c;可视化大屏的…

【发表案例】传感器网络及电路类,仅1个月26天录用

【期刊简介】IF&#xff1a;1.0-2.0&#xff0c;JCR4区&#xff0c;中科院4区 【检索情况】SCI 在检&#xff0c;正刊 【征稿领域】自主传感器网络的高级接口电路及其应用 【参考周期】2个月左右录用 【截稿日期】2023.1.31 重要时间节点&#xff1a;仅1个月26天录用 2022/12…

神经网络中常用的权重初始化方法及为何不能全初始化为0

1.权重初始化的重要性 神经网络的训练过程中的参数学习时基于梯度下降算法进行优化的。梯度下降法需要在开始训练时给每个参数赋予一个初始值。这个初始值的选取十分重要。在神经网络的训练中如果将权重全部初始化为0&#xff0c;则第一遍前向传播过程中&#xff0c;所有隐藏层…

深度学习笔记:感知机

感知机&#xff08;perceptron&#xff09;为神经网络的起源算法。感知机接受多个输入信号&#xff0c;输出一个信号。感知机信号只有0和1。 在上图的感知机中&#xff0c;x1和x2两个输入信号会分别乘以其对应权重(weight) w1和w2&#xff0c;传入神经元。神经元计算传来信号综…

Disentangled Face Attribute Editing via Instance-Aware Latent Space Search翻译

论文地址 代码地址 摘要 最近的研究表明&#xff0c;生成对抗网络&#xff08;GAN&#xff09;的潜空间中存在一组丰富的语义方向&#xff0c;这使得各种面部属性编辑应用成为可能。然而&#xff0c;现有的方法可能会遇到属性变化不好的问题&#xff0c;从而导致在更改所需属…

JS中数组对象使用

文章目录一、创建数组对象二、数组翻转1.检测数组2.翻转数组&#xff1a;三、添加数组元素1.push方法2.unshift方法四、删除数组元素1.pop方法2.shift方法&#x1f918;案例1五、数组排序六、数组索引方法1.indexof(数组元素)2.lastIndexOf方法&#x1f91f;案例2七、数组转化为…

数字验证学习笔记——SystemVerilog芯片验证16 ——约束控制块随机函数

一、约束块控制 一个类可以包含多个约束块。可以把不同约束块用于不同测试。一般情况下&#xff0c;各个约束块之间的约束内容是互相协调不违背的&#xff0c;因此通过随机函数产生随机数时可以找到合适的解 如果子类继承父类&#xff0c;也继承了父类的约束&#xff0c;这个时…

基于蒙特卡诺的电动汽车充电负荷曲线研究(充电开始时间,充电电量,充电功率)(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【C++】 STL-vector模拟实现

文章目录vector源码的内容:成员变量默认构造函数构造函数1-无参构造构造函数2 -使用n个相同的值构造构造函数3-使用迭代器区间构造拷贝构造函数**传统写法**现代写法赋值重载函数opeartor传统写法现代写法析构函数迭代器begin & end任意类型vector容器迭代器通用遍历方式:容…