【问题标题】:In Java Priority Queue implementation remove at method, why it does a sift up after a sift down?在 Java Priority Queue 实现 remove at 方法中,为什么它在筛选后进行筛选?
【发布时间】:2016-12-06 09:34:03
【问题描述】:

我一直在阅读 Java 集合 API,优先级队列部分,由两位大师 Josh Bloch 和 Doug Lea 实现。

Java PriorityQueue 是用数组堆实现的。

代码sn-ps在这里,来自PriorityQueue.java,第600行:

/**
 * Removes the ith element from queue.
 *
 * Normally this method leaves the elements at up to i-1,
 * inclusive, untouched.  Under these circumstances, it returns
 * null.  Occasionally, in order to maintain the heap invariant,
 * it must swap a later element of the list with one earlier than
 * i.  Under these circumstances, this method returns the element
 * that was previously at the end of the list and is now at some
 * position before i. This fact is used by iterator.remove so as to
 * avoid missing traversing elements.
*/

private E removeAt(int i) {
        // assert i >= 0 && i < size;
        modCount++;
        int s = --size;
       if (s == i) // removed last element
            queue[i] = null;
        else {
            E moved = (E) queue[s];
            queue[s] = null;
            siftDown(i, moved);
            //the code I am asking is below:
            if (queue[i] == moved) {
               siftUp(i, moved);
                if (queue[i] != moved)
                    return moved;
            }
        }
        return null;
    }

我想知道的是,曾经位于堆底部的移动元素应该是来自i 的子树中的一个大元素。 siftDown的方法是合理的,在一个siftDown之后,最小的子树会被提升到i的位置。

问题是,如果i没有改变,即siftDown之后移动的仍然存在,在我看来子树已经被堆化了,它不需要再次成为siftUp .

为什么乔希又把他们举到了顶端?

希望看过代码的朋友帮忙!

【问题讨论】:

    标签: java heap priority-queue


    【解决方案1】:

    问题是被移动的项目(queue[size-1] 的项目)可能与被删除的项目不在同一个子树中。考虑这个堆:

          0
      4       1
    5   6   2   3
    

    现在,如果您删除节点 5,您将拥有:

          0
      4       1
        6   2   3
    

    你把堆中的最后一个节点3,放到5所在的地方:

          0
      4       1
    3   6   2   
    

    你把 3 筛下来,但它已经是一片叶子了。它可能不合适。您必须筛选它才能获得:

          0
      3       1
    4   6   2   
    

    【讨论】:

    • 谢谢吉姆!堆一侧的大元素可能不如同一层中的其他元素大。我认为你描述的场景可能只发生在叶子级别。如果 siftDown 没有改变位置并且它有孩子,那么 siftUp 似乎是多余的。这是正确的吗?希望澄清。
    • 添加到前面的评论,特别是在基于数组的堆实现中,唯一的情况应该是叶子。
    • @stayclean:这不限于叶级别。在更大的堆中,它可能发生在叶子上方的许多级别。您应该能够使用仅比我的示例深一级的堆来构建这样的案例。
    • 感谢您的澄清。如果 siftdown 没有发生,则仅表示该节点是其子树中的前一个移动节点中最小的节点。这不能保证它大于它的父节点,移动节点和父键值之间应该有足够的间隙来容纳更多的自然数,这与堆另一侧的元素没有绝对关系。
    猜你喜欢
    • 2015-09-09
    • 2014-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-25
    • 2012-12-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多