堆(什么是堆以及怎样自己创建堆)

简介: 堆(什么是堆以及怎样自己创建堆)

什么是堆


堆(Heap)是一种数据结构,通常可以被视为一棵完全二叉树。在堆中,每个节点都满足一种特殊的条件,即父节点的键值总是大于或等于(小于或等于)任何一个子节点的键值,这被称为堆性质。堆是一种非常常见的数据结构,通常用于实现优先队列等应用程序。常见的堆分为两种,分别是最大堆和最小堆,最大堆的根节点的关键字要比它的每个子节点的关键字都要大;最小堆的根节点的关键字要比它的每个子节点的关键字都要小。


既然堆可以被视为完全二叉树,我们就需要知道:堆可以用数组来实现,在数组中,下标为i的节点的左节点为2i+1,右节点为2i+2,父节点为(i-1)/2向下取整。这个知识对我们创建堆的时候非常关键。

19.png

这个就是最大堆,根节点的关键字总是大于子节点的关键字

20.png

最小堆,根节点的关键字总是小于子节点的关键字。


如何创建堆

向下调整创建堆

假设给你一个数组{27,15,65,49,37,34,19,18,35},你需要根据数组里面的数组创建一个最小堆或最小堆。


我们先将数组转换为完全二叉树。

image.png

因为提供的数组是随机的,所以我们需要看每一个子树是否符合根节点的关键字大于子节点关键字。我们从二叉树的底部开始创建,将子节点中较大的节点的值与根节点比较,如果子节点的值大于根节点,就将子节点与父节点交换,交换完成后我们需要看子节点所在的子树是否还符合最大堆,所以就需要向下调整。

22.png

23.png

24.png

交换之后我们需要判断以15为父亲节点的子树是否符合最大堆

25.png

26.png

27.png

28.png

所以我们这个最终的二叉树就是我们需要的最大堆。

看看代码怎么写吧。

public class myHeap {
//因为堆也是数组,所以我们创建一个数组成员变量
    public int[] elem;
    //usedSize用来记录数组的大小
    public int usedSize;
    public myHeap() {
    //假设将数组大小定义为10
        this.elem = new int[10];
    }
    //初始化数组,将传入的数组赋值给elem
    public void initHeap(int[] array) {
        for (int i = 0; i < array.length; i++) {
            elem[usedSize++] = array[i];
        }
    }
    //创建
    public void creatHeap() {
    //从二叉树的底部开始创建,结束条件是parent = 0
        for (int parent = (usedSize-2)/2; parent >= 0 ; parent--) {
            shiftDown(parent,usedSize);
        }
    }
    private void shiftDown(int parent,int len) {
        int child = 2*parent+1;
        //当child  = len时,说明该子树已遍历完
        while(child < len) {
            if(child+1 < len && elem[child] < elem[child+1]) {
                child++;
            }
            if(elem[parent] < elem[child]) {
                int tmp = elem[parent];
                elem[parent] = elem[child];
                elem[child] = tmp;
                parent = child;
                child = 2*parent+1;
            }else {
                break;
            }
        }
    }
}

如果我们像创建最小堆,我们只需要将">“改成”<"就行了。


向上调整创建堆(堆的插入和删除)


如果我们不想你给我一整个数组之后我再创建堆,而是你给我一个数字我就创建堆,那么我们该怎么办呢?这里我们可以使用向上调整的思路来解决,因为我们一个一个添加数字,每次将数字添加在数组的最后,在添加了之后就会调整二叉树,所以就保证了在添加下一个数字之前,你的二叉树就已经满足了最大堆或最小堆,我们添加了一个数字之后就只需要向上做出调整即可。


在删除堆的时候我们默认删除数组的首元素,然后将数组的最后一个元素移动到数组的首地址处。移动之后我们再进行向下调整操作。


同样是这个数组{27,15,65,49,37,34,19,18,35},我们一个一个添加,看看怎么做吧。

29.png

30.png

31.png

32.png

33.png

34.png

35.png

36.png

重复此操作

37.png

38.png

40.png

向下调整

41.png

public class myHeap {
    public int[] elem;
    public int usedSize;
    public myHeap() {
        this.elem = new int[10];
    }
    private void shiftUp(int child) {
        int parent = (child-1)/2;
        while(child > 0) {
            if(elem[child] > elem[parent]) {
                int tmp = elem[parent];
                elem[parent] = elem[child];
                elem[child] = tmp;
                child = parent;
                parent = (child-1)/2;
            }else {
                break;
            }
        }
    }
    //插入
    public void offer(int data) {
        elem[usedSize++] = data;
        if(isFull()) {
            elem = Arrays.copyOf(elem,2*elem.length);
        }
        shiftUp(usedSize-1);
    }
    public boolean isFull() {
        return usedSize > elem.length;
    }
    //删除操作
    public void pop() {
        if(isEmpty()) {
            return;
        }
        swap(elem,0,usedSize-1);
        usedSize--;
        shiftDown(0,usedSize);
    }
    public boolean isEmpty() {
        return usedSize == 0;
    }
    public void swap(int[] array, int i, int j) {
        int tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }
}


相关文章
|
Web App开发 关系型数据库 RDS
电源缓启动(软起动)原理分享
该文讨论了电源的缓启动(软起动)技术,主要是为了解决热插拔时的电源振荡和冲击电流问题。缓启动电路有两个主要功能:防抖动延时上电和控制输入电流上升斜率。文章提到了电压斜率型缓启动电路,通过MOS管和相关电阻、电容元件实现延迟和电流控制。电路设计中,MOS管的栅极电压和漏源电流的变化决定了电流上升斜率,从而限制热插拔时的冲击。
752 0
|
JavaScript 前端开发 安全
模板引擎(art-template)详解
它采用作用域预声明来优化模板渲染速度,从而获得来接近JavaScript极限的运行性能,并同时支持nodejs和浏览器 1.1.特性 模板引擎是第三方模块,让开发者以更友好的方式拼接字符串,是代码啊更清晰,更加易于维护 1.2. 模板 art-template同时支持两种语法,标准语法可以让模板更容易读写, 原始语法具有强大的逻辑处理能力
1697 0
第k小的数(2种快排解法、1种堆排解法)
第k小的数(2种快排解法、1种堆排解法)
300 0
|
11月前
|
存储 网络协议 Linux
第七问:你了解大端和小端字节序吗?
大端和小端是计算机中数据存储的两种字节序方式。大端(Big Endian)将高位字节存储在低地址,小端(Little Endian)将低位字节存储在低地址。大端主要用于网络通信和某些文件格式,确保数据传输的一致性;小端广泛应用于本地计算和硬件优化,提高处理速度。现代大多数 PC 和嵌入式设备使用小端字节序,如 x86 和 ARM 架构。
|
6月前
|
Ubuntu 应用服务中间件 网络安全
关于一些轻量云服务器SSH断连的疑问
在使用2H2G配置的轻量级Ubuntu 22.04服务器时,按照Solana官网教程安装环境,执行`[cargo install]`命令(特别是安装avm和anchor包时),出现SSH连接中断且无法重新登录的问题。推测可能是低配服务器资源耗尽导致SSH进程被终止,即便CPU使用率下降也无法恢复连接,需重启服务器并等待约30分钟才能恢复正常。此现象或与服务器性能限制有关,期待更多测试与解释。
|
人工智能 文字识别 API
20行代码教你如何批量提取图片中文字
大家好,我是志斌~ 之前志斌在考研的时候遇到了一个问题,就是要将图片中的文字给提取出来,当时是J哥帮忙搞出来的,现在已经考完研了,也学会了提取方式,现在来给大家分享一下。
1363 0
20行代码教你如何批量提取图片中文字
|
存储 算法 搜索推荐
数据结构--堆的深度解析
数据结构--堆的深度解析
|
安全 网络协议 算法
电脑病毒木马的清除和防范方法
电脑病毒木马的清除和防范方法
2882 0
电脑病毒木马的清除和防范方法
WK
|
机器学习/深度学习 算法
什么是损失函数和损失函数关于参数的梯度
损失函数是机器学习中评估模型预测与真实值差异的核心概念,差异越小表明预测越准确。常见损失函数包括均方误差(MSE)、交叉熵损失、Hinge Loss及对数损失等。通过计算损失函数关于模型参数的梯度,并采用梯度下降法或其变种(如SGD、Adam等),可以优化参数以最小化损失,提升模型性能。反向传播算法常用于神经网络中计算梯度。
WK
533 0
|
存储 缓存 Linux
Python pip常用功能说明
pip 是 Python 的一个包管理工具,可以让用户方便地下载和安装 Python 包。pip 可以从 PyPI (Python Package Index) 上下载这些包,并且自动处理依赖关系。PyPI 是一个存储着 Python 包的仓库,用户可以从这个仓库中搜索、下载和安装 Python 包。在使用 pip 安装 Python 包时,由于 PyPI 的服务器位于国外,下载速度可能比较慢,因此我们可以使用国内的镜像源来提高下载速度。常见的国内镜像源有阿里云、清华大学等。
615 6