二叉堆的建立、插入、删除

一、基本概念:

1、完全二叉树:若二叉树的深度为h,则除第h层外,其他层的结点全部达到最大值,且第h层的所有结点都集中在左子树。

2、满二叉树:满二叉树是一种特殊的的完全二叉树,所有层的结点都是最大值。

二叉堆

二叉堆是基于完全二叉树的基础上,加以一定的条件约束的一种特殊的二叉树。

根据约束条件的不同,二叉堆又可以分为两个类型:
大顶堆 和小顶堆。

大顶堆

即任何一个父节点的值,都 大于等于 它左右孩子节点的值。

小顶堆

即任何一个父节点的值,都 小于等于 它左右孩子节点的值。 二叉堆的根节点叫做 堆顶 ,它是大顶堆里面的最大值,小顶堆里的最小值。

二、 构造最大堆

原始数据为a\[\] = {4, 1, 3, 2, 16, 9, 10, 14, 8, 7},采用顺序存储方式,对应的完全二叉树如下图所示:

1、基本思想

首先将每个叶子节点视为一个堆,再将每个叶子节点与其父节点一起构造成一个包含更多节点的堆。所以,在构造堆的时候,首先需要找到最后一个节点的父节点,从这个节点开始构造最大堆;直到该节点前面所有分支节点都处理完毕,这样最大堆就构造完毕了。

假设树的节点个数为n,以1为下标开始编号,直到n结束。对于节点i,其父节点为i/2;左孩子节点为i2,右孩子节点为i*2+1。最后一个节点的下标为n,其父节点的下标为n/2。

我们边针对上边数组操作如下图所示,最后一个节点为7,其父节点为16,从16这个节点开始构造最大堆;构造完毕之后,转移到下一个父节点2,直到所有父节点都构造完毕。

2、代码实现

因为堆是基于完全二叉树,所以我们不需要用链式结构来表示,我们可以直接用数组存。

假设父节点的下表为parent,从父节点获取子节点:

左节点下标: 2 parent+1*

右节点下标: 2 parent+2*

假设子节点的下标为son(左右子节点都可以):

父节点下标:(son-1)/2

所以我们可以非常简单的表示出节点之间的关系了,下面是代码:

ini 复制代码
public class MaxHeap {

    // 数据元素个数
    private int heapSize;

    // 存放元素的空间大小
    private int maxSize;
    // 数据元素的存放
    Integer[] data;

    /**
     * 构构造函数
     * @param pData
     * @param maxSize
     */
    public MaxHeap(Integer[] pData,int maxSize) {
        this.maxSize = maxSize;
        data = new Integer[maxSize];
        for (int i = 0; i < pData.length; i++) {
            data[i] =pData[i];
        }
        this.heapSize = pData.length;
        // 初始化最大堆
        initMapHeap(heapSize -1);
    }

    //获取左节点
    private int getLeft(int parent)
    {
        return 2 * parent + 1;
    }
    //获取右节点
    private int getRight(int parent)
    {
        return 2 * parent + 2;
    }
    private int getParent(int son) //获取父节点
    {
        if (son <= 0) {
            return -1;
        }
        return (son - 1) / 2;
    }

    /**
     * 初始化最大堆
     */
    private void initMapHeap(int index) {

        while (index > 0 ) {
            int parentIndex = getParent(index);
            // 当前父节点,有右节点和左节点
            if (getRight(parentIndex) < heapSize) {
                Integer leftData = data[getLeft(parentIndex)];
                Integer rightData = data[getRight(parentIndex)];
                Integer ParentData = data[parentIndex];
                // 左边节点 > 右边节点
                if (leftData >= rightData) {
                    // 左边节点,还大于 父节点
                    if (leftData > ParentData) {
                        // 左节点和父节点,交换数据
                        data[parentIndex] = leftData;
                        data[getLeft(parentIndex)] = ParentData;
                        // 每次比较完成后,后面的节点还要进行比较,直接都进行重新比较
                        index = heapSize -1;

                    }
                } else {
                    // 右边节点,> 左边节点,> 父节点点
                    if (rightData > ParentData ) {
                        // 右节点和父节点交换数据
                        data[parentIndex] = rightData;
                        data[getRight(parentIndex)] = ParentData;
                        // 每次比较完成后,后面的节点还要进行比较,直接都进行重新比较
                        index = heapSize -1;
                    }
                }
            } else {
                // 只有左节点
                Integer leftData = data[getLeft(parentIndex)];
                Integer ParentData = data[parentIndex];
                // 左边节点,还大于 父节点
                if (leftData > ParentData) {
                    // 左节点和父节点,交换数据
                    data[parentIndex] = leftData;
                    data[getLeft(parentIndex)] = ParentData;

                    index = heapSize -1;
                }
            }
            index--;
        }
    }

    /**
     * 获取堆的大小
     * @return
     */
    public int getHeapSize() {
        return heapSize;
    }


    /**
     * 获取堆的数据
     * @return
     */
    public Integer[] getData() {
        Integer[] integer = new Integer[heapSize];
        for (int i = 0; i < heapSize; i++) {
            integer[i] = data[i];
        }
        return integer;
    }
}

三、 插入节点

最大堆的插入节点的思想就是先在堆的最后添加一个节点,然后沿着堆树上升。跟最大堆的初始化过程大致相同。

scss 复制代码
/***
 * 插入数据
 * @param num
 */
public void insertData(Integer num) {
    heapSize++;
    data[heapSize-1] = num;
    initMapHeap(heapSize -1);
}

四、顶节点删除

最大堆堆顶节点删除思想如下:将堆树的最后的节点提到根结点,然后删除最大值,然后再把新的根节点放到合适的位置。

scss 复制代码
/***
 * 删除数据
 * @param num
 */
public void del(Integer num) {
   // 找到要删除元素的索引
    int i = 0;
    for (; i < heapSize; i++) {
        if (data[i].intValue()  == num.intValue()) {
            break;
        }
    }
    // 删除索引处值,
    for(int j = i ;j < heapSize -1;j++) {
        data[j] = data[j+1];
    }
    // 最后一个值,置空
    data[heapSize -1] = null;
    // 大小-1
    heapSize--;
    // 重新初始化
    initMapHeap(heapSize -1);
}
相关推荐
程序猿DD16 小时前
OctaFuse Gateway 2.15.0:MCP 聚合、向量 Embeddings 与官方目录同步
后端·agent
蛋蛋的就会顺顺的18 小时前
hot100 p0——栈,贪心算法,动态规划
java·前端·javascript·算法·leetcode·力扣
余槐i18 小时前
PostgreSQL 函数索引报错 must be immutable:3种绕法与代价
数据库·后端·sql·postgresql·性能优化
需要82618 小时前
Spring Bean 生命周期:从实例化到销毁的完整钩子
java·后端·spring
小 黄 鸡19 小时前
HashMap 面试必会知识点详解(JDK 8)
java·面试·职场和发展
凤山老林20 小时前
Spring Boot 接入国密 SM2/SM3/SM4 的一些实践记录
spring boot·后端·python
伯恩bourne21 小时前
Spring Boot Bean 注册的两种方式:@Component 家族 vs @Configuration + @Bean
java·spring boot·后端
张彦峰ZYF21 小时前
停顿短不等于延迟低:G1、ZGC 与低延迟服务的目标分野、代价账本与选型标定
后端·回收暂停、应用停止总时长·到达安全点、分配停滞·屏障吃吞吐、并发线程吃核心·并发窗口吃堆余量·分区式靠挑回收集兑现目标·染色指针式不支持压缩引用
silvamux_21 小时前
curl 测试 OpenAI 兼容 API 的完整流程
java·人工智能·缓存·ai·oneapi
pride.li21 小时前
第03章 Python 教程-基础语法:注释与变量
服务器·前端·后端·python·算法·计算机视觉