[Java Collections Framework] 浅析java 集合框架(四) : priorityQueue

priorityQueue 是一个堆 (heap) 的数据结构,底层就是一个数组,逻辑上是一棵完全二叉树。并且PQ的堆顶idx 是0。也就是说 对于任意的节点, x x , lc=2x+1,rc=2(x+1),par=(x1)>>1, 这里算法的算法设计和本科数据结构教材相同,我们重点看,这几个算法,siftUP,siftDown,heapify

    private void siftUp(int k, E x) {
        if (comparator != null)
            siftUpUsingComparator(k, x);
        else
            siftUpComparable(k, x);
    }

    @SuppressWarnings("unchecked")
    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;
    }

    @SuppressWarnings("unchecked")
    private void siftUpUsingComparator(int k, E x) {
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            Object e = queue[parent];
            if (comparator.compare(x, (E) e) >= 0)// 当前元素大于等于父亲 ok
                break;
            queue[k] = e;
            k = parent;
        }
        queue[k] = x;
    }

    /**
     * Inserts item x at position k, maintaining heap invariant by
     * demoting x down the tree repeatedly until it is less than or
     * equal to its children or is a leaf.
     *
     * @param k the position to fill
     * @param x the item to insert
     */
    private void siftDown(int k, E x) {
        if (comparator != null)
            siftDownUsingComparator(k, x);
        else
            siftDownComparable(k, x);
    }

    @SuppressWarnings("unchecked")
    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)
                break;
            queue[k] = c;
            k = child;
        }
        queue[k] = key;
    }

    @SuppressWarnings("unchecked")
    private void siftDownUsingComparator(int k, E x) {
        int half = size >>> 1;
        while (k < half) {
            int child = (k << 1) + 1;
            Object c = queue[child];
            int right = child + 1;
            if (right < size &&
                comparator.compare((E) c, (E) queue[right]) > 0)// 选择孩子中最小的那个和当前元素(k,E) 比较
                c = queue[child = right];
            if (comparator.compare(x, (E) c) <= 0)// x <= child ,ok满足性质
                break;
            queue[k] = c;
            k = child;
        }
        queue[k] = x;
    }

    /**
     * Establishes the heap invariant (described above) in the entire tree,
     * assuming nothing about the order of the elements prior to the call.
     */
    @SuppressWarnings("unchecked")
    private void heapify() {
        for (int i = (size >>> 1) - 1; i >= 0; i--)
            siftDown(i, (E) queue[i]);
    }

siftUp(int idx,E e) 的意思是假设 节点 e 当前在 idx 将他不破坏堆的性质,向上滑动,我们可以看见里面分提供comparator 和comparable 对象提供了封装方法。其他就没什么好说的了,任何一本数据结构书籍都会讲的。注意这里直接从已知集合构造堆的时间复杂度是 O(n) O ( n ) , 原因在这个 heapify 方法,他从最后一个非叶子节点开始逐个向下滑动,对于一颗完全二叉树来说,这样的移动次数不会超过 O(n) O ( n ) , 这个是很好证明的,假设完全二叉树共 n n 个节点, k<=logN 层,第 i i 层元素为 2(i1) ,则最多需要移动

i=1k12i1(ki)=O(2k)=O(n) ∑ i = 1 k − 1 2 i − 1 ∗ ( k − i ) = O ( 2 k ) = O ( n )

具体计算我省略了。

由于上面两个方法 siftUp,siftDown 都是 O(logN) O ( l o g N ) 的,所以凡是仅仅和 idx 相关的操作复杂度都为 O(logN)(e.g.: add,poll)这两个都是对一个是对最后一个元素的操作另一个是对堆顶元素的操作,都可以仅通过 siftUP 或者siftDown 完成。

不过需要注意 remove(Obj) 的复杂度是 O(n) O ( n ) 的,因为他要先遍历整个堆然后再删除。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值