JavaDS--构造堆及其方法

JDK1.8中的PriorityQueue底层使用了堆的数据结构,而堆实际就是在完全二叉树的基础之上进行了一些元素的调整。
new出的空间在堆、优先级队列底层结构也是堆,此处的两个堆不是一个概念

  • new–>堆:指的是一块具有特殊作业的内存空间
  • 优先级队列底层的堆:值得是一种数据结构

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

性质:

  • 堆中某个节点的值总是不大于或不小于其父节点的值;
  • 堆总是一棵完全二叉树。

存储方式:
堆是一棵完全二叉树,因此可以层序的规则采用顺序的方式来高效存储,
注意:
对于非完全二叉树,则不适合使用顺序方式进行存储,因为为了能够还原二叉树,空间中必须要存储空节点,就会导致空间利用率比较低
在这里插入图片描述

  • 如果i为0,则i表示的节点为根节点,否则i节点的双亲节点为 (i - 1)/2
  • 如果2 * i + 1 小于节点个数,则节点i的左孩子下标为2 * i + 1,否则没有左孩子
  • 如果2 * i + 2 小于节点个数,则节点i的右孩子下标为2 * i + 2,否则没有右孩子

构建堆

实现Comp接口
用户可自己选择构建大堆还是小堆

interface Comp
{
    public int compare(int left, int right);
}

// 按照小于的方式进行比较
class Less implements Comp{
    // 0: left == right
    // > 0: left > right
    // < 0: left < right
    public int compare(int left, int right){
        return  left - right;
    }
}

class Greater implements Comp{
    public int compare(int left, int right){

        return  right - left;
    }
}

创建堆

public class Test {
    private  int[] array;
    private  int size;
    Comp compare=null;
    public Test(Comp com){
        //默认的构造--将其底层容量设置为11
        array=new int[11];
		compare = com;
    }
    public Test(int initCapacity, Comp com){
        if(initCapacity < 1){
            //标准库:抛出一个非法参数异常
            initCapacity=11;
        }
        array=new int[initCapacity];
        compare = com;
    }
    public Test(int[] arr, Comp com){
        //注意标准库中没有该接口---标准库中可以采用集合来构造优先级队列
        array=new int[arr.length];
        for(int i=0;i<arr.length;i++){
            array[i]=arr[i];
            size++;
        }
        size = arr.length;
        compare = com;
        //将array中的元素调整,让其满足小堆的性质
        int lastLeaf=(array.length-2)>>1;
        for(int root =lastLeaf;root>=0;root--){
            shiftDown(root);
        }
}        

peek方法:

    int peek(){
        return array[0];
    }

扩容
源码中优先级队列的扩容:

  • 如果容量小于64时,是按照oldCapacity*2 + 2的方式扩容的
  • 如果容量大于等于64,是按照oldCapacity*2的方式扩容的
  • 如果容量超过MAX_ARRAY_SIZE,按照MAX_ARRAY_SIZE来进行扩容
    private void grow(){
        int oldC=array.length;
        int newC=oldC+((oldC<64)?(oldC+2):(oldC>>1));
        array=Arrays.copyOf(array,newC);
    }

offer方法
首先要判断是否扩容
注意:堆在底层是将其元素按照完全二叉树的方式存储
1.将元素放入到底层的数组中---->尾插
2.检测新元素加入是否破坏堆的性质

    boolean offer(int x){
        grow();
        array[size++]=x;
        shiftUp(size-1);
        return true;
    }

    private  void shiftUp(int child){
        int parent=(child-1)>>1;
        while (parent>=0){
        	//比较孩子和双亲的大小
        	if(compare.compare(array[child], array[parent]) < 0){
            //if(array[child]<array[parent]){
                swap(child,parent);
                //交换后可能使上层元素不满足小堆
                child=parent;
                parent=(child-1)>>1;
            }else{
                return;
            }
        }
    }

poll方法
删除:删除的是堆顶的元素
方式一:直接将后序所有元素向前搬移一个位置,虽然可以删除元素,但搬移之后不满足堆的性质,效率低
方式二:
1.将堆顶元素与堆中最后一个元素进行交换,堆元素个数-1
2.将堆顶元素向下调整(交换后对顶元素的两个子树都满足小堆的条件)

    //删除堆顶的元素
    int poll(){
        int ret=array[0];
        swap(0,size-1);
        size--;
        shiftDown(0);
        return ret;
    }

isEmpty方法

    boolean isEmpty(){

        return 0==size;
    }

向下调整
如果只有100这一个节点不满足小堆的性质,要让其满足堆的性质,只要对100进行调整
将100放在子树的某个位置---->即向下调整
在这里插入图片描述
1.parent表示本次需要调整的节点的下标
2.调整以parent为根的二叉树
3.注意:调整之前,一定要保证parent的左右子树一定满足小堆的性质
4.如果要检测parent是否满足小堆的性质,只需要使用parent与其孩子进行比较
6.满足小堆性质–>说明以parent为根的二叉树已经是小堆
7.不满足小堆性质–>说明parent比其孩子大,此时需要将parent与其较小的孩子进行交换交换完以后,parent较大的元素向下移动,可能导致其子树不满足小堆性质,需要继续调整其子树


    private void shiftDown(int parent){
        //使用child标记parent的较小的孩子
        //默认情况下先让其标记做孩子,因为parent可能只有做孩子
        int child =parent*2+1;
        int size=array.length;
        while (child<size){
            //找较小的孩子
            //在通过左右孩子比较前,必须要保证有孩子存在--while循环条件已经保证做孩子存在
            //if(child+1<size && array[child+1]<array[child]){
            if(child+1 < size && compare.compare(array[child+1], array[child]) < 0){
           
                child += 1;
            }
            //检查双亲是否比较小的孩子大
            if(array[parent]>array[child]){
                //说明parent不满足小堆的性质--交换
                swap(parent,child);
                //parent较大的元素向下移动可能会导致子树不满足堆的性质
                parent=child;
                child=parent*2+1;
            }else{
                //以parent为根二叉树已经满足堆的性质
                return;
            }
        }
	    private void swap(int parent,int child){
	        int tmp=array[parent];
	        array[parent]=array[child];
	        array[child]=tmp;
	    }

向上调整
将如图所示的二叉树调整成小堆
在这里插入图片描述
必须找到倒数第一个非叶子节点
lastleaf:最后一个节点的下标为size-1,
倒数第一格非叶子节点刚好是最后一个节点的双亲
lastleaf = (size-2)>>1

        //将array中的元素调整,让其满足小堆的性质
        int lastLeaf=(array.length-2)>>1;
        for(int root =lastLeaf;root>=0;root--){
            shiftDown(root);
        }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值