[JAVA数据结构]堆

news2024/11/27 18:33:20

目录

1.堆的概念

2.堆的创建

3.堆的插入与删除

3.1堆的插入

3.2堆的删除


1.堆的概念

如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为 小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆

 注意:堆是一棵完全二叉树

2.堆的创建

        在堆这种数据结构中,可以看成每三个元素构成的一个子树为单位来观察。所以我们要调节的是每一个子树的父亲节点与孩子节点之间的关系

        对于大根堆,我们需要维护父亲节点比孩子节点都大

        对于小根堆,我们需要维护父亲节点比孩子节点都小

        我们可以从最后一棵子树开始,先得到其父亲节点与孩子节点,再比较其关系进行位置上的调整。调整完此子树后,因为我们是从下往上开始遍历,变动了一棵树的位置就可能影响到其他子树的关系,所以我们需要维护其下方子树也应为一个大/小根堆

        需要将父亲节点的位置移动到孩子节点的位置,孩子节点移动到下一棵子树的孩子节点位置中

对于父亲节点与孩子节点的数组下标关系为:

(在找孩子节点的时候,因为堆是一棵完全二叉树的原因,可能没有右孩子。所以我们首先搜寻的是其左孩子节点)

parent:父亲节点的下标

child:左孩子节点的下标

parent = (child - 1) /  2;

chile = parent * 2 + 1;

下面是建立大根堆的图演示 

 ​​

代码演示:

import java.util.Arrays;

public class Heap {
    public int[] elem;
    public static int usedSize;

    public Heap(int[] elem) {
        this.elem = new int[10];//初始长度为10,后面可以考虑扩容
    }

    //创建一个大根堆
    public void createHeap(int[] array){
        //首先要放到elem数组中,还可以得到其元素个数
        for(int i = 0; i < array.length; i++){
            this.elem[i] = array[i];
            usedSize++;
        }
        //开始创建大根堆
        //首要要找到其最后一棵子树的父亲节点
        //parent = (child - 1) / 2
        //最后一棵子树,肯定包含有数组中的最后一个节点
        //所以我们可以通过最后一个元素的下标来得到父亲节点的下标
        //从最后一棵子树开始,遍历每一颗子树,每一颗子树父亲节点在数组中相邻
        //直到父亲节点的下标小于0下标
        for(int parent = (usedSize - 1 - 1) / 2; parent >= 0; parent--){
            //我们将维护子树状态的方法称为向下调整
            shiftDown(parent,usedSize);
        }
    }
    //向下调整
    private void shiftDown(int parent, int len) {
        //向下调整是,维护子树的状态
        //因为调整了节点的关系,可能也会影响其他子树,所以要调整与此子树相关的下方的子树
        //可以用过parent = child的方法来跳跃到其他子树
        //直到child越界

        int child = parent * 2 + 1;//得到左子树的下标
        while(child < len){
            //第一步,查看左右孩子节点的大小关系
            //按需则取,再与父亲节点比较
            //当然是在确保拥有右孩子的前提下
            if(child + 1 < len && elem[child] < elem[child + 1]){
                //因为我们创建的是大根堆
                //在右孩子比左孩子大的时候,我们的child位置需要跳转到右孩子
                child = child + 1;//child的位置从左孩子跳到右孩子
            }
            //再与父亲节点比较
            if(elem[child] > elem[parent]){
                //交换
                int tmp = elem[child];
                elem[child] = elem[parent];
                elem[parent] = tmp;
                //向下调整
                //父亲节点跳转到孩子节点
                //孩子节点根据跳转后的父亲节点的下标进行转换
                parent = child;
                child = parent * 2 + 1;
            }else{
                break;//因为我们是从下往上调整的,只要此树满足了大根堆
                //就不需要变动节点的位置,也就意味着不会影响已经调整好了的其他子树
                //所以可以直接break
            }
        }
    }

    public static void main(String[] args) {
        int[] array = {1,5,3,7,8,6};
        Heap heap = new Heap(array);
        heap.createHeap(array);
        System.out.println(Arrays.toString(heap.elem));
    }
}

 结果为:

3.堆的插入与删除

3.1堆的插入

        堆的插入是插在队尾的,即当作此数组的最后一个元素。

        插入一个元素后,要维持其依然为一个大/小根堆,所以需要调整。但元素是在队尾的,就不能用之前的向下调整了,此处运用的是向上调整。将插入的元素逐步向上转移,安插到一个合适的位置。

以大根堆为例:

 代码演示:

    public void push(int val){
        if(isFull()){//如果满了就要扩容
            elem = Arrays.copyOf(elem,2*elem.length);
        }
        this.elem[usedSize++] = val;
        shiftUp(usedSize - 1);
    }

    public void shiftUp(int child){
        int parent = (child - 1) / 2;
        //实际上就是把新插入的数值当成child,一直推着其往上移动,直到找到一个合适的地方
        while(child > 0){
            if(elem[child] > elem[parent]){
                int tmp = elem[child];
                elem[child] = elem[parent];
                elem[parent] = tmp;
                child = parent;
                parent = (child - 1) / 2;
            }else{
                break;
            }
        }
    }

    public boolean isFull() {
        return usedSize == elem.length;
    }

3.2堆的删除

        关于堆的删除,堆只能删除其最后一个元素。

        首先把最后一个元素放到0下标即堆顶的位置,再使usedSize-1,达到删除的效果.

        为了维持堆的特性,我们将新换到堆顶的元素使用向下调整,将其安插到合适的地方

    public void poll() {
        if(empty()) {
            throw new RuntimeException("堆为空");
        }
        //交换最后一个元素和第一个元素的位置
        int tmp = elem[0];
        elem[0] = elem[usedSize-1];
        elem[usedSize-1] = tmp;
        usedSize--;//总元素-1,无法读取,达到删除的效果
        shiftDown(0,usedSize);//将新换到堆顶的元素向下调整
    }

    public boolean empty() {
        return usedSize == 0;
    }

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

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

相关文章

【Linux】远程桌面连接服务器报错:未启用对服务器的远程访问......

&#x1f341;博主简介 &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01; 文章目录 前述操作环境说明&#xff1a;远程报…

<数据结构>NO4.带头双向循环链表

文章目录 前言1. 头文件2. 函数实现1&#xff09;创建哨兵位节点2&#xff09;新增一个节点3&#xff09;打印链表4&#xff09;头插5&#xff09;尾插6&#xff09;头删7&#xff09;尾删8&#xff09;查找9&#xff09;pos前插入10&#xff09;删除pos处节点11&#xff09;销…

Redis 缓存穿透、缓存击穿与缓存雪崩

文章目录 1. 缓存穿透解决方法 2. 缓存击穿解决方法 3. 缓存雪崩解决方法 在 redis 的应用场景中&#xff0c;需要考虑缓存在某些场景下可能出现的问题&#xff1a; 缓存穿透 缓存击穿 缓存雪崩 以下缓存问题的讨论都是基于以下应用架构讨论的&#xff1a; 1. 缓存穿透 对应…

数据备份系列:Rsync 备份实战记录(二)

一、Rsync Cron 场景使用 在对数据备份要求实时性不高的情况下&#xff0c;可优先考虑该场景&#xff0c;选择一个合适的时间&#xff0c;对数据进行定时远程增量同步。 在《数据备份系列&#xff1a;Rsync 备份详解&#xff08;一&#xff09;》中我们已经对服务搭建以及远程…

DAD-DAS模型

DAD-DAS模型 文章目录 DAD-DAS模型[toc]1 产品服务:需求方程2 实际利率:费雪方程3 通货膨胀:菲利普斯方程4 预期通货膨胀&#xff1a;适应性预期5 货币政策规则&#xff1a;泰勒方程6 动态总供给-总需求方程&#xff08;DAS-DAD&#xff09;7 总供给冲击模拟 1 产品服务:需求方…

【JavaEE初阶】文件操作——IO

摄影分享~ 文章目录 文件文件路径&#xff08;Path&#xff09; 文件的类型Java中操作文件File概述 文件内容的读写——数据流字节流InputStream概述OutputStream 概述字符流FileInputStream 概述利用 Scanner 进行字符读取 实例练习 文件 文件&#xff1a;File这个概念&…

PostSQL内存管理之内存上下文

瀚高数据库 目录 环境 文档用途 详细信息 环境 系统平台&#xff1a;Linux x86-64 Red Hat Enterprise Linux 7 版本&#xff1a;14 文档用途 了解pg内存分配 详细信息 1.MemoryContex机制 内存上下文是pg相关的内存控制结构&#xff0c;树形结构组织下的内存上下文能在频繁的…

SNMPc软件的下载和安装教程,计算机网络管理,网络工程师

⬜⬜⬜ &#x1f430;&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;(*^▽^*)欢迎光临 &#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;&#x1f430;⬜⬜⬜ ✏️write in front✏️ &#x1f4dd;个人主页&#xff1a;陈丹宇jmu &am…

vue 改变数据后,数据变化页面不刷新

文章目录 导文文章重点方法一&#xff1a;使用this.$forceUpdate()强制刷新方法二&#xff1a;Vue.set(object, key, value)方法三&#xff1a;this.$nextTick方法四&#xff1a;$set方法 导文 在vue项目中&#xff0c;会遇到修改完数据&#xff0c;但是视图却没有更新的情况 v…

让开发者成为创新主体 | 阿里云云原生4月动态

作者&#xff1a;云原生内容小组 云原生月度动态 ✦ 云原生是企业数字创新的最短路径。 《阿里云云原生每月动态》&#xff0c;从趋势热点、产品新功能、服务客户、开源与开发者动态等方面&#xff0c;为企业提供数字化的路径与指南。 本栏目每月更新。 01 趋势热点 &…

vue - 实现登录后用户无操作后自动退出登录功能,当用户鼠标不动、键盘不动、无窗口滚动时自动清除登录状态(可自定义删减条件,详细示例源码一键复制开箱即用)

需求 很多教程都是无效而且有bug。。很难用索性自己搞了最健壮的解决方案。 在vue项目中,实现自动检测用户没有【移动鼠标】【操作键盘】【窗口滚动】时,自动清除登录信息强制退出登录下线,支持自定义触发时间(比如无操作10分钟就执行),自定义条件(比如只监听用户鼠标是…

匿名对象以及临时空间

目录 大纲 1.何为匿名对象 2.产生匿名对象的四种情况&#xff1a; 1&#xff09;给初始化对象时 2&#xff09;以值的方式给函数传参&#xff1b; 3&#xff09;类型转换&#xff1b; 4&#xff09;函数返回时&#xff1b; 3.编译器优化 I.在同一行代码的优化 II.在函…

电脑关机很慢怎么办?这5个方法很有用!

案例&#xff1a;电脑关机很慢怎么办&#xff1f; 【我的电脑才买来不久&#xff0c;现在每次关机都很慢&#xff0c;有时甚至一直在转圈圈无法关机&#xff0c;怎么处理这种情况呢&#xff1f;】 如果使用电脑时间长了&#xff0c;我们可能会发现电脑的各项性能都会有所下降…

Vue3(5)插槽Slots

目录 一、插槽内容与出口 二、渲染作用域 三、默认内容 四、具名插槽 五、作用域插槽 六、具名作用域插槽 一、插槽内容与出口 在之前的博文中&#xff0c;我们已经了解到组件能够接收任意类型的JS值作为props&#xff0c;但组件要如何接收模板内容呢&#xff1f;在某些…

图片堆叠、多重聚焦的几种办法

当拍摄的物品较小&#xff0c;景深较深时&#xff0c;相机的焦点只能放在较近或者较远的一处&#xff0c;图片的整个画面就不能保证完全清晰&#xff0c;多重聚焦的原理其实就是拼合&#xff0c;在画幅的不同处拍摄聚焦图片&#xff0c;将各个聚焦的内容拼合在一起&#xff0c;…

杂记 2023.5.11

目录 come across(as).. 与异性对话经验和理论、策略 单词记忆 机器学习 come across(as).. 这个用法在口语里超级高频&#xff0c;表示「给人.印象&#xff0c;让人觉得..」&#xff0c;s后面可跟名词、形容词、 being形容词。 我们再来看几个例子&#xff1a; ◆He comes ac…

【Leetcode -455.分发饼干 -459.重复的字符串】

Leetcode Leetcode -455.分发饼干Leetcode - 459.重复的字符串 Leetcode -455.分发饼干 假设你是一位很棒的家长&#xff0c;想要给你的孩子们一些小饼干。但是&#xff0c;每个孩子最多只能给一块饼干。 对每个孩子 i&#xff0c;都有一个胃口值 g[i]&#xff0c;这是能让孩…

多语言的APP外包开发流程

很多创业的人希望把APP做成多国家使用的模式&#xff0c;尤其是一些小游戏开发&#xff0c;很多小游戏玩法全世界都是一样的&#xff0c;这样开发一次就可以在全球推广。在开发这种类型软件的过程中需要注意哪些呢&#xff0c;今天和大家分享这方面的知识。北京木奇移动技术有限…

C语言入门篇——字符串,内存函数

目录 1、字符串函数 1.1求字符串长度 1.1.1strlen函数 1.2长度不受限制的字符串函数 1.2.1strcpy函数 1.2.2strcat函数 1.2.3strcmp函数 1.3长度受限制的字符串函数介绍 1.3.1strncpy函数 1.3.2strncat函数 1.3.3strncmp函数 1.4字符串查找 1.4.1strstr函数 1.4.…

JavaScript实现输入年龄来判断年龄阶段是青年/中年/老年人的代码

以下为实现输入年龄来判断年龄阶段是青年/中年/老年人的程序代码和运行截图 目录 前言 一、实现输入年龄来判断年龄阶段是青年/中年/老年人 1.1 运行流程及思想 1.2 代码段 1.3 JavaScript语句代码 1.4 运行截图 前言 1.若有选择&#xff0c;您可以在目录里进行快速查找…