二叉堆的节点插入、删除以及构建过程

目录

什么是二叉堆

二叉堆的基本操作 

1.插入节点

2.删除节点

3.构建二叉堆

代码实现 


什么是二叉堆

定义:二叉堆,本质上是一种完全二叉树。

分类:二叉堆分为最大堆和最小堆两种类型,最大堆和最小堆分别又可称为大顶堆和小顶堆。最大堆中,任何一个父节点的值都大于或等于它的左、右孩子节点的值;最小堆中,任何一个父节点的值都小于或等于它的左、右孩子节点的值。

二叉堆的根节点叫做堆顶。因此,最大堆的堆顶是整个堆中的最大元素,最小堆的堆顶是整个堆中的最小元素。

 

二叉堆的基本操作 

二叉堆的基本操作也是二叉堆的自我调整,有插入节点、删除节点和构建二叉堆。

1.插入节点

 二叉堆要插入一个节点时,总是插在完全二叉树的最后一个位置。插入过程为:

若在最小堆中插入一个节点,则插入节点后与父节点进行比较,若小于父节点则将该节点“上浮”,然后继续与父节点比较,重复操作,直到稳定(即满足最小堆中的所有父节点大于等于其左右节点),最后堆顶节点元素必定是最小的元素。在最大堆中插入节点,则是当大于父节点时“上浮”,最后的堆顶元素必定是最大的元素。

由于每次插入操作,都是单一节点在进行浮动,因此,插入操作的时间复杂度是O(logn)

2.删除节点

二叉堆要删除一个节点时,总是删除堆顶的节点。删除过程为:

若在最小堆中删除一个节点,我们移除堆顶的节点,并将堆中的最后一个节点临时补到原本堆顶的位置,然后将堆顶节点与其左右节点进行比较,如果其中最小的一个比堆顶节点小,则将堆顶节点“下沉”,然后继续与左右节点进行比较,重复操作,直到稳定。在最大堆中删除一个节点时,则是将左右节点中大的那个,并且比父节点还大的那个节点进行移动,将父节点“下沉”,重复操作,直到稳定。

由于删除节点操作,是单一节点的“下沉”,因此,删除操作的时间复杂度为O(logn)

3.构建二叉堆

构建二叉堆,就是把一个无序的完全二叉树调整为二叉堆,本质上就是让所有非叶子节点依次“下沉”(构建最小堆)。

如下图所示,依次将2,8,3,6节点进行下沉操作,顺序如下所示。

节点“3”需要继续下沉: 

构建二叉堆的时间复杂度为O(n),这里就不进行数学推理了。 

代码实现 

由于二叉堆从本质上来说就是一颗完全二叉树,因此采用数组进行存储。

以下的代码中依次展示了下沉节点、上浮节点以及构建二叉堆的过程,其中举的例子为最小堆

有一点需要注意:上浮节点不一定是一定用在构建最小堆,即“上浮”不一定指的是小值上浮。

import java.util.Arrays;

public class MyHeap {
    //下沉调整,将某个节点进行下沉,可以用于构建一个最小堆以及删除一个节点的情况
    public static void downAdjust(int[] array, int parentIndex, int length){
        int temp = array[parentIndex];
        int childIndex = 2 * parentIndex + 1;
        while(childIndex < length){
            //如果有右孩子,且右孩子小于左孩子的值,则定位到右孩子
            if(childIndex + 1 < length && array[childIndex + 1] < array[childIndex]){
                childIndex++;//childIndex取到的是左右孩子中最小的那个
            }

            //如果父节点小于任何一个孩子的值,则直接跳出
            if(temp <= array[childIndex])
                break;
            //因为是单一节点的下沉,所以无须真正交换,单向赋值即可
            array[parentIndex] = array[childIndex];
            parentIndex = childIndex;
            childIndex = 2 * childIndex + 1;
        }
        array[parentIndex] = temp;
    }

    //上浮调整,将最后一个节点进行上浮,可以用在节点插入中
    public static void upAdjust(int[] array){
        int childIndex = array.length - 1;
        int parentIndex = (childIndex - 1) / 2;
        //temp保存插入的叶子节点值,用于最后的赋值
        int temp = array[childIndex];
        while(childIndex > 0 && temp < array[parentIndex]){
            //单向赋值
            array[childIndex] = array[parentIndex];
            childIndex = parentIndex;
            parentIndex = (parentIndex - 1) / 2;
        }
        array[childIndex] =  temp;
    }

    public static void buildHeap(int[] array){
        //从最后一个非叶子节点开始,依次下沉
        //最后一个非叶子节点为最后一个节点的父节点
        for(int i = (array.length - 2) / 2; i >= 0; i--){
            downAdjust(array, i, array.length);
        }
    }

    public static int[] insertHeap(int[] array, int data){
        //插入一个节点,用新数组表示,然后对新数组进行上浮操作
        int newLength = array.length + 1;
        int[] newArray = new int[newLength];
        System.arraycopy(array,0,newArray,0,array.length);
        newArray[newLength - 1] = data;
        upAdjust(newArray);
        return newArray;
    }

    public static int[] deleteHeap(int[] array){
        int newLength = array.length - 1;
        int[] newArray = new int[newLength];
        newArray[0] = array[array.length - 1];
        System.arraycopy(array,1,newArray,1,array.length-2);
        downAdjust(newArray,0,newLength);
        return newArray;
    }

    public static void main(String[] args) {
        int[] array = new int[] {1,3,2,6,5,7,8,9,10,0};
        upAdjust(array);
        System.out.println(Arrays.toString(array));

        array = new int[] {7,1,3,10,5,2,8,9,6};
        buildHeap(array);
        System.out.println(Arrays.toString(array));

        //当前的array是一个最小堆,进行插入节点操作
        System.out.println(Arrays.toString(insertHeap(array,4)));

        //当前的array是一个最小堆,进行删除节点操作
        System.out.println(Arrays.toString(deleteHeap(array)));
    }
}

 

  • 7
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值