优先级队列-源码小记

本文详细解析了Java中的优先级队列PriorityQueue,包括其基于堆排序的底层实现、构造函数的三种情况、堆化过程以及出队和入队的操作。核心方法siftDown和siftUp确保了队列的小顶堆特性。通过对这些关键方法的分析,展示了PriorityQueue如何维护元素的优先级顺序。
摘要由CSDN通过智能技术生成

目录

1.概述

2.重要方法解读

2.1 构造函数 

2.2 方法“堆化”

2.3 出队

2.4 入队

2.5 其他的方法

3.小结


1.概述

  1.  优先级队列的底层实现是基于堆排序
  2. 所谓优先级队列,其全局的优先级是通过一步一步访问队头元素之后呈现的,在某一时刻,其队列中的堆从上到下是不严格有序的,这是堆的性质导致的,因为堆只要求父子之间的有序,而不要求兄弟之间的有序。也就是说,队列中出队头元素外的元素们之间在还没有出队时不是严格有序的。其实它们都没有内部排队。
  3. 成员:
    1. 一个数组 Object[],名叫queue,用于存放数据,按照完全二叉树的形式,且queue[0]是队头。即 queue[parent]的两个孩子是 queue[2*parent+1]、queue[2*parent+2]
    2. 一个比较器 Comparator

2.重要方法解读

2.1 构造函数 

    public PriorityQueue(Collection<? extends E> c) {
        if (c instanceof SortedSet<?>) {
        // code 1
            SortedSet<? extends E> ss = (SortedSet<? extends E>) c;
            this.comparator = (Comparator<? super E>) ss.comparator();
            initElementsFromCollection(ss);
        }
        else if (c instanceof PriorityQueue<?>) {
        // code 2
            PriorityQueue<? extends E> pq = (PriorityQueue<? extends E>) c;
            this.comparator = (Comparator<? super E>) pq.comparator();
            initFromPriorityQueue(pq);
        }
        else {
            this.comparator = null;
        // code 3 实际上的初始化方法
            initFromCollection(c);
        }
    }
  1. code1 按照有序集合来初始化。
  2. code2 按照优先级队列来初始化,内部直接是把入参优先级队列深copy类一下。
  3. code3 是实际的初始化方:保存元素到queue数组中,并“堆化”(优先级队列的“本质”)。

2.2 方法“堆化”

    private void heapify() {
        // 从倒数第一个非叶子节点(i = (size >>> 1) - 1)开始 siftDown
        for (int i = (size >>> 1) - 1; i >= 0; i--)
            siftDown(i, (E) queue[i]);
    }
    /**
    * 把元素x放到k的位置上。是堆化的原子操作——重要!重要!重要!
    */
    private void siftDownComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>)x;
        int half = size >>> 1;        // loop while a non-leaf
        while (k < half) {
            int child = (k << 1) + 1; // assume left child is least
            Object c = queue[child];
            int right = child + 1;
            if (right < size &&
                ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)
                c = queue[child = right];
            // 找左孩子或者有孩子较小的那个,作为待比较对象和待赋的值。
            if (key.compareTo((E) c) <= 0)
            // 要比孩子小——满足小顶堆的规则,否则就不需再”siftDown“了。
                break;
            queue[k] = c;
            k = child;
        }
        // 最后把插入元素x放到其应该的位置k处。这个函数可以看做一个大的swap(a, b)。
        queue[k] = key;
    }
  1. 以上是堆排序的对核心的代码,当然也是优先级队列的。
  2. 堆化之后,该队就是小顶堆了。下面我们看出队和入队

2.3 出队

    public E poll() {
        if (size == 0)
            return null;
        int s = --size;
        modCount++;
        // code1
        E result = (E) queue[0];
        E x = (E) queue[s];
        queue[s] = null;
        if (s != 0)
        // code2
            siftDown(0, x);
        return result;
    }
  1. code1,就是直接返回队头元素 queue[0]
  2. code2,把最后一个元素从队头处插入——注意不是插到队头
  3. siftDown函数在上面已经分析过,就是自上而下的调整成堆,是堆化的原子操作。

2.4 入队

    public boolean offer(E e) {
        if (e == null)
            throw new NullPointerException();
        modCount++;
        int i = size;
        if (i >= queue.length)
            grow(i + 1);
        size = i + 1;
        if (i == 0)
            queue[0] = e;
        else
        // code1 自下往上的调整
            siftUp(i, e);
        return true;
    }
    /**
    * 自下往上的调整。把元素x从位置k插入。开始时k是队尾位置,入队嘛,不能插队的。
    */
    private void siftUpComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>) x;
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            Object e = queue[parent];
            if (key.compareTo((E) e) >= 0)
            // 插入的元素要大于其父节点,否则就不要再往上调整了,级别不够了。
                break;
            queue[k] = e;
            k = parent;
        }
        queue[k] = key;
    }
  1. siftUp是与siftDown相反的防线进行调整,调整的目标一致——堆化,但是具体动作不同,代码中已有注释。
  2. siftUp是仅次于siftDown重要的操作单元了。

2.5 其他的方法

        主要方法已经讲完了,其他的方法也分类:

  1. 广义的重载方法,比如add(Element)/peek()等
  2. 画蛇添足的方法,比如remove(Object),因为对于队列来讲,就是入队出队。
  3. 比较基础的方法,比如size()/toArray()

3.小结

 

        本文并没有深入讲解二叉堆的数据结构和原理,但是都有涉及,特别是siftDown和siftUp操作;此外也在此基础上对优先级队列的代码进行了整体视角的阅读。

        推荐阅读:JAVA中优先队列PriprityQueue详解

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值