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

一、基本概念:

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);
}
相关推荐
TangKenny12 分钟前
计算网络信号
java·算法·华为
肘击鸣的百k路13 分钟前
Java 代理模式详解
java·开发语言·代理模式
城南vision19 分钟前
Docker学习—Docker核心概念总结
java·学习·docker
wyh要好好学习26 分钟前
SpringMVC快速上手
java·spring
尢词28 分钟前
SpringMVC
java·spring·java-ee·tomcat·maven
Mr. zhihao35 分钟前
享元模式在 JDK 中的应用解析
java·享元模式
茶馆大橘38 分钟前
微服务系列五:避免雪崩问题的限流、隔离、熔断措施
java·jmeter·spring cloud·微服务·云原生·架构·sentinel
wrx繁星点点38 分钟前
享元模式:高效管理共享对象的设计模式
java·开发语言·spring·设计模式·maven·intellij-idea·享元模式
真的想不出名儿42 分钟前
Java基础——反射
java·开发语言
鱼跃鹰飞44 分钟前
大厂面试真题-简单说说线程池接到新任务之后的操作流程
java·jvm·面试