java中priorityQueue的理解与使用 | 堆的创建向下调整向上调整

一:PriorityQueue

1.引言

前面学习了Stack和Queue,其实还有一种特殊的队列叫PriorityQueue,即优先级队列。

优先级队列的作用是保证每次取出的元素都是队列中权值最小的(java中是最小的,C++中是最大的)

  • 数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象。 这种数据结构就是优先级队列(Priority Queue)

2.关于Priority Queue 使用时要注意

  1. 不能放入null对象,否则会报NUllPointrException
  2. 插入的元素必须是能够比较大小的,否则会报ClassCastException
  3. 内部可以自动扩容
  4. 使用时必须导包: import java.util.Priority.Queue;
  5. 插入与删除的时间复杂度是o(logn)
  6. 底层使用了堆的结构,具体说是通过完全二叉树实现的小项堆

3.常用接口介绍

(1)构造方法:

public class TestPriorityQueue {
    public static void main(String[] args) {

        //(1)创建一个空的优先级队列,底层默认容量即为11
        PriorityQueue<Integer> q1 = new PriorityQueue<>();

        //(2)创建一个带有容量的优先级队列
        PriorityQueue<Integer> q2 = new PriorityQueue<>(100);


        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(2);

        //(3)用一个集合来创建优先级队列
        PriorityQueue<Integer> q3 = new PriorityQueue<>(list);
        System.out.println(q3.size());
        System.out.println(q3.peek());
    }
}

(2)具体方法

 使用:

  public static void main(String[] args) {

        int[] arr = {4, 1, 9, 3, 2, 5};
        PriorityQueue<Integer> q = new PriorityQueue<>(arr.length);
        //一般如果知道元素个数建议直接将底层容量给好,下来就不需要多的扩容(扩容效率会变低)
        for (int e : arr) {
            q.offer((e));//循环依次入队列
        }
        System.out.println(q.size());
        System.out.println(q.peek());
        q.poll();//删除两个元素
        q.poll();
        System.out.println(q.size());
        System.out.println(q.poll());

        q.offer(0);
        System.out.println(q.peek());

        q.clear();
        System.out.println(q.peek());
        if(q.isEmpty()){
            System.out.println("优先级队列已空!");
        }else{
            System.out.println("优先级队列不空!");
        }

    }

4.扩容简括

  •  如果容量小于64时,是按照oldCapacity的2倍方式扩容的
  • 如果容量大于等于64,是按照oldCapacity的1.5倍方式扩容的
  • 如果容量超过MAX_ARRAY_SIZE,按照MAX_ARRAY_SIZE来进行扩容

5.优先级队列的模拟实现

PriorityQueue底层使用了堆的数据结构,而堆实际就是在完全二叉树的基础之上进行了一些元素的调 整。下面我们先来认识堆

二:堆Heap

堆:一个完全二叉树,并满足每个节点比其孩子大或者小,则为堆

 1.堆的创建

(1)堆向下调整

要会堆的创建,则先得了解这个知识点,我们拿一道特殊的创建例子来举例:

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

 仔细观察上图后发现:根节点的左右子树已经完全满足堆的性质,因此只需将根节点向下调整好即可。

1.让parent标记要调整的跟节点,child标记其左孩子

2.如果左孩子存在,则进行以下操作:

(1)parent右孩子是否存在,存在找到左右孩子中最小的孩子,让child进行标

(2)将parent与较小的孩子child比较,并交换

  3.交换后可能不满足堆的性质,则继续向下走,进行向下调整

//向下调整过程
    private static void shiftDown(int parent, int arr[]){
        //先标记左孩子,只可能现有左孩子
        int child = parent*2+1;
        int size = arr.length;
        //如果左孩子存在的话
        while(child<size){
            //看右孩子是否存在,存在则返回二者中较小的一个
            if (child + 1 < size && arr[child+1]<arr[child]){
                size += 1;
            }
            //如果双亲比孩子还小,说明满足堆的特性
            if(arr[parent] < arr[child]){
                break;
            }else{
                int tmp=arr[child];
                arr[child]=arr[parent];
                arr[parent]=tmp;

                //继续向下移动
                parent=child;
                child=parent*2+1;
            }
        }
    }

(2)堆的创建

对于普通的序列{ 1,5,3,8,7,6 },即根节点的左右子树不满足堆的特性,又该如何调整呢?

思路:我们可以找倒数第一个非叶子节点,从该节点位置开始往前一直到根节点,遇到一个节点,应用向下调整 。

 public static void createHeap(int[] array) {
        // 找倒数第一个非叶子节点,从该节点位置开始往前一直到根节点,遇到一个节点,应用向下调整
        int root = (((array.length-1)-1)>>1);
        for (; root >= 0; root--) {
            shiftDown( root,array);
        }
    }

2.堆的实现

offer(E e):向优先级队列插入元素

值得注意的是,新加入的元素可能会破坏小顶堆的性质,因此需要做一定的调整

 //插入一个元素
    boolean offer (Integer e){
        if(e == null){
            throw new NullPointerException("插入时元素为空");
        }
        ensureCapacity();
        array[size++]=e;
        shiftUp(size-1);
        return true;
    }
    //向上调整
    private void shiftUp(int child){
        int parent = (child -1)/2;
        while(child!=0){//当比较到根结点时循环停止
            if(array[child]<array[parent]){
                swap(child,parent);
                child = parent;
                parent = (child-1)/2;
            }else{
                return;
            }
        }
    }
    //扩容
    private void ensureCapacity(){
        if(array.length == size){
            int newCapacity = array.length * 2;
            array = Arrays.copyOf(array,newCapacity);
        }
    }

poll(E e):获取并删除首元素

由于删除会改变堆的结构,为维护小项堆的性质,需要进行一定的调整

注意:堆的删除一定删除的是堆顶元素。

具体如下: 1. 将堆顶元素对堆中最后一个元素交换 2. 将堆中有效数据个数减少一个 3. 对堆顶元素进行向下调整

 //向下调整过程
    private static void shiftDown(int parent, int arr[]){
        //先标记左孩子,只可能现有左孩子
        int child = parent*2+1;
        int size = arr.length;
        //如果左孩子存在的话
        while(child<size){
            //看右孩子是否存在,存在则返回二者中较小的一个
            if (child + 1 < size && arr[child+1]<arr[child]){
                size += 1;
            }
            //如果双亲比孩子还小,说明满足堆的特性
            if(arr[parent] < arr[child]){
                break;
            }else{
                int tmp=arr[child];
                arr[child]=arr[parent];
                arr[parent]=tmp;

                //继续向下移动
                parent=child;
                child=parent*2+1;
            }
        }
    }
    public void swap(int left,int right){
        int tmp = left;
        left = right;
        right = tmp;
    }
    //删除栈顶元素
    public Integer poll(){
        if(size == 0){
            return 0;
        }
        Integer ret = array[0];
        swap(array[0],array[size-1]);
        size--;
        shiftDown(0,array);
        return ret;
    }

3.堆的应用

1)用堆做底层封装优先级队列

2)堆排序 (建堆-->删除堆顶元素)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值