堆的创建---java

目录

1、堆的概念

 2.堆的存储方式

 3、堆的创建

3.1、堆向下调整

3.2、堆的创建


1、堆的概念

如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为 小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆的性质:
1.堆中某个节点的值总是不大于或不小于其父节点的值;
2.堆总是一棵完全二叉树。

 2.堆的存储方式

堆在逻辑上是一棵完全二叉树,在物理上是以层序的规则采用顺序的方式高效存储的。

 将元素存储到数组中后,假设i为节点在数组中的下标,根据树的性质则有:
如果i为0,则i表示的节点为根节点,否则i节点的双亲节点为 (i - 1)/2
如果2 * i + 1 小于节点个数,则节点i的左孩子下标为2 * i + 1,否则没有左孩子
如果2 * i + 2 小于节点个数,则节点i的右孩子下标为2 * i + 2,否则没有右孩子

 3、堆的创建

3.1、堆向下调整

 对于集合{ 27,15,19,18,28,34,65,49,25,37 }中的数据,如果将其创建成堆呢?

由图可知,根节点的左右子树已经完全满足堆的性质,因此只需将根节点向下调整好即可。

(堆向下调整必须满足大部分结点已满足堆的性质,只需要调整小部分即可)

向下过程(以小堆为例):
1.先判断parent是不是叶子结点(即判断有没有左孩子(叶子结点满足堆的性质))

2.判断有没有右孩子,如果有,找出值最小的那个结点与parent比较,如果比parent小,则交换

3.如果没有右子树,判断parent与左孩子的大小

4.循环检查调整完后,剩余子树是否满足堆的性质


递归实现:

    //传入数组以及待调整的结点的下标
    public static void adjustDown递归(int[] arr,int parentIdx){
        int childLeftIdx=2*parentIdx+1;
        int size=arr.length;
        //1.说明没有左孩子,是一个叶子结点
        if(childLeftIdx>=size){
            return;
        }
        int childRightIdx=2*parentIdx+2;   //定义右孩子
        int minIdx=childLeftIdx;   //先假定左右孩子中最小的是左孩子
        //如果右子树存在且右子树比左子树小,重置最小节点
        if(childRightIdx<size&&arr[childLeftIdx]>arr[childRightIdx]){
            minIdx=childRightIdx;
        }
        //比较最小值结点与待调整结点的大小
        if(arr[parentIdx]<arr[minIdx]){
            return;
        }
        if(arr[parentIdx]>arr[minIdx]){
            int tmp=arr[parentIdx];
            arr[parentIdx]=arr[minIdx];
            arr[minIdx]=tmp;
        }
        //再递归判断被交换的结点是否满足堆的性质
        adjustDown递归(arr,minIdx);
    }

非递归实现:

    //非递归实现
    //传入数组及待调整的下标
    public static void adjustDown(int[] arr,int parentIdx){
        //先判断是不是叶子结点
        int size=arr.length;
        while(2*parentIdx+1<size){
            int minIdx=2*parentIdx+1;   //假定最小值结点是左孩子
            int childRightIdx=2*parentIdx+2;
            //更新最小值下标
            if(childRightIdx<size&&arr[2*parentIdx+1]>arr[childRightIdx]){
                minIdx=childRightIdx;
            }
            if(arr[parentIdx]<arr[minIdx]){
                return;
            }
            if(arr[parentIdx]>arr[minIdx]){
                int tmp=arr[parentIdx];
                arr[parentIdx]=arr[minIdx];
                arr[minIdx]=tmp;
            }
            //更新下一个要调整的结点下标
            parentIdx=minIdx;
        }
    }


 3.2、堆的创建

堆向下调整需要满足的条件太过于苛刻,对于一个无序的数组来说显然不合适,那么该如何来调整呢?

找倒数第一个非叶子节点,从该节点位置开始往前一直到根节点,遇到一个节点,应用向下调整

以建小堆为例:{8,3,6,7,5,1}

代码如下:

    public static void createHeap小堆(int[] arr){
        //找到最后一个非叶子结点,从该结点一直往前到根节点,对每一个遇到的结点向下调整
        int size=arr.length;
        int lastIdx=(size-1-1)/2;   //就是找最后一个结点的父节点
        for(int i=lastIdx;i>=0;i--){
            adjustDown(arr,i);
        }
    }

 

建堆的时间复杂度:O(n) 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值