一、简介
  • 是一种树状的数据结构
  • 常见的堆有:
    1. 二叉堆(完全二叉堆,其逻辑结构就是一棵完全二叉树)
    2. 多叉堆
    3. 索引堆
    4. 二项堆
    5. 斐波那契堆
    6. 左斜堆
  • 性质
    1. 任意节点的值总是 >= 或 <= 子节点的值
    2. 如果任意结点的值 >= 子节点的值,则称为大根堆或最大堆
    3. 如果任意结点的值 <= 子节点的值,则成为小根堆或最小堆
    4. 上一层的值不一定全都大于下一层的值。
    在这里插入图片描述
二、二叉堆(数组存放,最大堆为例)

在这里插入图片描述

  • 数组索引的规律(n是元素数量)
  1. 如果 i=0 ,则为根结点
  2. i > 0,父结点索引为(i-1)/2,向下取整
  3. 如果 2i+1 <= n-1,他的左子节点索引为 2i+1
  4. 如果 2i+1 > n-1,则无左子节点
  5. 如果 2i+2 <= n-1,他的右子节点索引为 2i+2
  6. 如果 2i+2 > n-1,则无右子节点
  • 插入操作 O(logn):直接插入到数组的最后,然后执行上滤操作,即跟父结点比较,如果比父节点大则交换位置
    public static void shiftUp(int[] nums,int index){
        //存储当前结点的值
        int temp=nums[index];
        while(index > 0){
            //寻找父结点
            int parentIndex = (index-1)>>1;
            //如果父结点大于当前结点,跳出循环
            if(temp <= nums[parentIndex]){
                break;
            }
            //将父节点的值赋给子节点
            nums[index] = nums[parentIndex];
            //指向新的当前结点
            index = parentIndex;
        }
        //给当前结点赋值
        nums[index]=temp;
    }
  • 删除操作 O(logn):堆中的删除是指删除根结点,具体步骤为用堆中最后一个元素覆盖根元素,然后执行下滤操作,即将当前结点与子结点进行比较,如果 >= 子结点或者无子结点则不做任何操作,如果小于子节点,则与子节点中大的那个子结点交换位置。
    public static void shiftDown(int[] nums,int index){
        //计算第一个叶子结点的索引,此后所有结点均为叶子节点
        // 此处应为(堆的元素个数/2),这里直接使用length不对,因为length是死的
        int half=(nums.length)>>1;
        int temp=nums[index];
        //如果有子节点
        while(index < half){
            //index的结点有两种情况
            //1.只有左子结点
            //2.同时有左右子结点
            //默认与左子结点进行比较
            int childIndex=(index<<1)+1;
            //取出左子节点
            int child=nums[childIndex];
            //右子结点
            int rightIndex=childIndex+1;
            //选出左右子结点中大的那一个
            if(rightIndex<nums.length && nums[rightIndex]>child){
                child=nums[childIndex=rightIndex];
            }

            //如果子结点的值<=当前结点,则跳出循环
            if(temp >= nums[childIndex]) {
                break;
            }
            //上移子结点
            nums[index] = child;
            index=childIndex;
        }
        nums[index]=temp;
    }
  • 原地建堆(给出一个数组使其变为堆)
    有两种方法:
    1. 自上而下的上滤
    2. 自下而上的下滤
public class Heap {
    public static void main(String[] args) {
        int[] nums=new int[5];
        nums[0] = 0;
        nums[1] = 1;
        nums[2] = 2;
        nums[3] = 3;
        nums[4] = 4;
        //自下而上的下滤,即从第一个最后一个非叶结点开始下滤
        for(int i=(nums.length>>1)-1; i >= 0; i--) {
            shiftDown(nums, i);
        }
        //自上而下的上滤,
        for(int i=1; i < nums.length; i++) {
            shiftUp(nums, i);
        }
    }

    /***
    * @Description: 让index位置的元素上过滤
    * @Param: []
    * @return: void
    */
    public static void shiftUp(int[] nums,int index){
        //存储当前结点的值
        int temp=nums[index];
        while(index > 0){
            //寻找父结点
            int parentIndex = (index-1)>>1;
            //如果父结点大于当前结点,跳出循环
            if(temp <= nums[parentIndex]){
                break;
            }
            //将父节点的值赋给子节点
            nums[index] = nums[parentIndex];
            //指向新的当前结点
            index = parentIndex;
        }
        //给当前结点赋值
        nums[index]=temp;
    }

    /***
    * @Description: 让index位置的元素下滤
    * @Param: [nums, index]
    * @return: void
    */
    public static void shiftDown(int[] nums,int index){
        //计算第一个叶子结点的索引,此后所有结点均为叶子节点
        // 此处应为(堆的元素个数/2),这里直接使用length不对,因为length是死的
        int half=(nums.length)>>1;
        int temp=nums[index];
        //如果有子节点
        while(index < half){
            //index的结点有两种情况
            //1.只有左子结点
            //2.同时有左右子结点
            //默认与左子结点进行比较
            int childIndex=(index<<1)+1;
            //取出左子节点
            int child=nums[childIndex];
            //右子结点
            int rightIndex=childIndex+1;
            //选出左右子结点中大的那一个
            if(rightIndex<nums.length && nums[rightIndex]>child){
                child=nums[childIndex=rightIndex];
            }

            //如果子结点的值<=当前结点,则跳出循环
            if(temp >= nums[childIndex]) {
                break;
            }
            //上移子结点
            nums[index] = child;
            index=childIndex;
        }
        nums[index]=temp;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值