Saturday, August 15, 2015

Java PriorityQueue Heap Implementation



Updating Java PriorityQueue when its elements change priority
You have to remove and re-insert, as the queue works by putting new elements in the appropriate position when they are inserted. This is much faster than the alternative of finding the highest-priority element every time you pull out of the queue. The drawback is that you cannot change the priority after the element has been inserted. A TreeMap has the same limitation (as does a HashMap, which also breaks when the hashcode of its elements changes after insertion).

JDK Code - Default, it's MinHeap.
poll or peek will return null(not throw exception)if there is no more element.
public class PriorityQueue<E> extends AbstractQueue<E>

    implements java.io.Serializable {
    private static final int DEFAULT_INITIAL_CAPACITY = 11;

    /**
     * Priority queue represented as a balanced binary heap: the two
     * children of queue[n] are queue[2*n+1] and queue[2*(n+1)].  The
     * priority queue is ordered by comparator, or by the elements'
     * natural ordering, if comparator is null: For each node n in the
     * heap and each descendant d of n, n <= d.  The element with the
     * lowest value is in queue[0], assuming the queue is nonempty.
     */
    transient Object[] queue; // non-private to simplify nested class access
     * The maximum size of array to allocate.
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    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
            siftUp(i, e);
        return true;
    }

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

    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;
    }
    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)
                break;
            queue[k] = e;
            k = parent;
        }
        queue[k] = x;
    }

    public E peek() {
        return (size == 0) ? null : (E) queue[0];
    }
    public E poll() {
        if (size == 0)
            return null;
        int s = --size;
        modCount++;
        E result = (E) queue[0];
        E x = (E) queue[s];
        queue[s] = null;
        if (s != 0)
            siftDown(0, x);
        return result;
    }
    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)
                c = queue[child = right];
            if (comparator.compare(x, (E) c) <= 0)
                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.
     */
    private void heapify() {
        for (int i = (size >>> 1) - 1; i >= 0; i--)
            siftDown(i, (E) queue[i]);
    }



    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        // Write out element count, and any hidden stuff
        s.defaultWriteObject();

        // Write out array length, for compatibility with 1.5 version
        s.writeInt(Math.max(2, size + 1));

        // Write out all elements in the "proper order".
        for (int i = 0; i < size; i++)
            s.writeObject(queue[i]);
    }
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read in size, and any hidden stuff
        s.defaultReadObject();

        // Read in (and discard) array length
        s.readInt();

        queue = new Object[size];

        // Read in all elements.
        for (int i = 0; i < size; i++)
            queue[i] = s.readObject();

        // Elements are guaranteed to be in "proper order", but the
        // spec has never explained what that might be.
        heapify();
    }
    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);
            if (queue[i] == moved) {
                siftUp(i, moved);
                if (queue[i] != moved)
                    return moved;
            }
        }
        return null;
    }

    private int indexOf(Object o) {
        if (o != null) {
            for (int i = 0; i < size; i++)
                if (o.equals(queue[i]))
                    return i;
        }
        return -1;
    }
    private void grow(int minCapacity) {
        int oldCapacity = queue.length;
        // Double size if small; else grow by 50%
        int newCapacity = oldCapacity + ((oldCapacity < 64) ?
                                         (oldCapacity + 2) :
                                         (oldCapacity >> 1));
        // overflow-conscious code
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        queue = Arrays.copyOf(queue, newCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

    public PriorityQueue(Collection<? extends E> c) {
        if (c instanceof SortedSet<?>) {
            SortedSet<? extends E> ss = (SortedSet<? extends E>) c;
            this.comparator = (Comparator<? super E>) ss.comparator();
            initElementsFromCollection(ss);
        }
        else if (c instanceof PriorityQueue<?>) {
            PriorityQueue<? extends E> pq = (PriorityQueue<? extends E>) c;
            this.comparator = (Comparator<? super E>) pq.comparator();
            initFromPriorityQueue(pq);
        }
        else {
            this.comparator = null;
            initFromCollection(c);
        }

    }
    private void initFromPriorityQueue(PriorityQueue<? extends E> c) {
        if (c.getClass() == PriorityQueue.class) {
            this.queue = c.toArray();
            this.size = c.size();
        } else {
            initFromCollection(c);
        }
    }

    private void initElementsFromCollection(Collection<? extends E> c) {
        Object[] a = c.toArray();
        // If c.toArray incorrectly doesn't return Object[], copy it.
        if (a.getClass() != Object[].class)
            a = Arrays.copyOf(a, a.length, Object[].class);
        int len = a.length;
        if (len == 1 || this.comparator != null)
            for (int i = 0; i < len; i++)
                if (a[i] == null)
                    throw new NullPointerException();
        this.queue = a;
        this.size = a.length;

    }
    private void initFromCollection(Collection<? extends E> c) {
        initElementsFromCollection(c);
        heapify();

    }
}
http://www.cnblogs.com/tstd/p/5125949.html
     为了维护这个特点,二叉堆在添加元素的时候,需要一个"上移"的动作,什么是"上移"呢,我们继续用图来说明。


11     public boolean offer(E e) {
12         // 如果元素e为空,则排除空指针异常
13         if (e == null)
14             throw new NullPointerException();
15         // 修改版本+1
16         modCount++;
17         // 记录当前队列中元素的个数
18         int i = size ;
19         // 如果当前元素个数大于等于队列底层数组的长度,则进行扩容
20         if (i >= queue .length)
21             grow(i + 1);
22         // 元素个数+1
23         size = i + 1;
24         // 如果队列中没有元素,则将元素e直接添加至根(数组小标0的位置)
25         if (i == 0)
26             queue[0] = e;
27         // 否则调用siftUp方法,将元素添加到尾部,进行上移判断
28         else
29             siftUp(i, e);
30         return true;
31     }
 4     private void siftUp(int k, E x) {
 5         // 如果比较器comparator不为空,则调用siftUpUsingComparator方法进行上移操作
 6         if (comparator != null)
 7             siftUpUsingComparator(k, x);
 8         // 如果比较器comparator为空,则调用siftUpComparable方法进行上移操作
 9         else
10             siftUpComparable(k, x);
11     }
12 
13     private void siftUpComparable(int k, E x) {
14         // 比较器comparator为空,需要插入的元素实现Comparable接口,用于比较大小
15         Comparable<? super E> key = (Comparable<? super E>) x;
16         // k>0表示判断k不是根的情况下,也就是元素x有父节点
17         while (k > 0) {
18             // 计算元素x的父节点位置[(n-1)/2]
19             int parent = (k - 1) >>> 1;
20             // 取出x的父亲e
21             Object e = queue[parent];
22             // 如果新增的元素k比其父亲e大,则不需要"上移",跳出循环结束
23             if (key.compareTo((E) e) >= 0)
24                 break;
25             // x比父亲小,则需要进行"上移"
26             // 交换元素x和父亲e的位置
27             queue[k] = e;
28             // 将新插入元素的位置k指向父亲的位置,进行下一层循环
29             k = parent;
30         }
31         // 找到新增元素x的合适位置k之后进行赋值
32         queue[k] = key;
33     }
34 
35     // 这个方法和上面的操作一样,不多说了
36     private void siftUpUsingComparator(int k, E x) {
37         while (k > 0) {
38             int parent = (k - 1) >>> 1;
39             Object e = queue[parent];
40             if (comparator .compare(x, (E) e) >= 0)
41                 break;
42             queue[k] = e;
43             k = parent;
44         }
45         queue[k] = x;
46     }
     对于二叉堆的出队操作,出队永远是要删除根元素,也就是最小的元素,要删除根元素,就要找一个替代者移动到根位置,相对于被删除的元素来说就是"下移"。
 4     public E remove() {
 5         E x = poll();
 6         if (x != null)
 7             return x;
 8         else
 9             throw new NoSuchElementException();
10     }
11 
12     /**
13      * 删除并返回队头的元素,如果队列为空则返回null
14      */
15    public E poll() {
16         // 队列为空,返回null
17         if (size == 0)
18             return null;
19         // 队列元素个数-1
20         int s = --size ;
21         // 修改版本+1
22         modCount++;
23         // 队头的元素
24         E result = (E) queue[0];
25         // 队尾的元素
26         E x = (E) queue[s];
27         // 先将队尾赋值为null
28         queue[s] = null;
29         // 如果队列中不止队尾一个元素,则调用siftDown方法进行"下移"操作
30         if (s != 0)
31             siftDown(0, x);
32         return result;
33     }
34 
35     /**
36      * 上移,x表示队尾的元素,k表示被删除元素在数组的位置
37      */
38     private void siftDown(int k, E x) {
39         // 如果比较器comparator不为空,则调用siftDownUsingComparator方法进行下移操作
40         if (comparator != null)
41             siftDownUsingComparator(k, x);
42         // 比较器comparator为空,则调用siftDownComparable方法进行下移操作
43         else
44             siftDownComparable(k, x);
45     }
46 
47     private void siftDownComparable(int k, E x) {
48         // 比较器comparator为空,需要插入的元素实现Comparable接口,用于比较大小
49         Comparable<? super E> key = (Comparable<? super E>)x;
50         // 通过size/2找到一个没有叶子节点的元素
51         int half = size >>> 1;        // loop while a non-leaf
52         // 比较位置k和half,如果k小于half,则k位置的元素就不是叶子节点
53         while (k < half) {
54              // 找到根元素的左孩子的位置[2n+1]
55             int child = (k << 1) + 1; // assume left child is least
56              // 左孩子的元素
57             Object c = queue[child];
58              // 找到根元素的右孩子的位置[2(n+1)]
59             int right = child + 1;
60             // 如果左孩子大于右孩子,则将c复制为右孩子的值,这里也就是找出左右孩子哪个最小
61             if (right < size &&
62                 ((Comparable<? super E>) c).compareTo((E) queue [right]) > 0)
63                 c = queue[child = right];
64             // 如果队尾元素比根元素孩子都要小,则不需"下移",结束
65             if (key.compareTo((E) c) <= 0)
66                 break; 
67             // 队尾元素比根元素孩子都大,则需要"下移"
68             // 交换跟元素和孩子c的位置
69             queue[k] = c;
70             // 将根元素位置k指向最小孩子的位置,进入下层循环
71             k = child;
72         }
73         // 找到队尾元素x的合适位置k之后进行赋值
74         queue[k] = key;
75     }
76 
77     // 这个方法和上面的操作一样,不多说了
78     private void siftDownUsingComparator(int k, E x) {
79         int half = size >>> 1;
80         while (k < half) {
81             int child = (k << 1) + 1;
82             Object c = queue[child];
83             int right = child + 1;
84             if (right < size &&
85                 comparator.compare((E) c, (E) queue [right]) > 0)
86                 c = queue[child = right];
87             if (comparator .compare(x, (E) c) <= 0)
88                 break;
89             queue[k] = c;
90             k = child;
91         }
92         queue[k] = x;
93     }
     int a = [7, 6, 5, 12, 10, 3, 1, 11, 15, 4 ];
  我们观察下用数组a建成的二叉堆,很明显,对于叶子节点4、15、11、1、3来说,它们已经是一个合法的堆。所以只要最后一个节点的父节点,也就是最后一个非叶子节点a[4]=10开始调整,然后依次调整a[3]=12,a[2]=5,a[1]=6,a[0]=7,分别对这几个节点做一次"下移"操作就可以完成了堆的构造。ok,我们还是用图解来分析下这个过程。
5     private void heapify() {
6         for (int i = (size >>> 1) - 1; i >= 0; i--)
7             siftDown(i, (E) queue[i]);
8     }
1 private void heapify() {
2         for (int i = (size >>> 1) - 1; i >= 0; i--)
3             siftDown(i, (E) queue[i]);
4     }
  int i = (size >>> 1) - 1,这行代码是为了找寻最后一个非叶子节点,然后倒序进行"下移"siftDown操作,是不是很显然了。

Labels

Review (572) System Design (334) System Design - Review (198) Java (189) Coding (75) Interview-System Design (65) Interview (63) Book Notes (59) Coding - Review (59) to-do (45) Linux (43) Knowledge (39) Interview-Java (35) Knowledge - Review (32) Database (31) Design Patterns (31) Big Data (29) Product Architecture (28) MultiThread (27) Soft Skills (27) Concurrency (26) Cracking Code Interview (26) Miscs (25) Distributed (24) OOD Design (24) Google (23) Career (22) Interview - Review (21) Java - Code (21) Operating System (21) Interview Q&A (20) System Design - Practice (20) Tips (19) Algorithm (17) Company - Facebook (17) Security (17) How to Ace Interview (16) Brain Teaser (14) Linux - Shell (14) Redis (14) Testing (14) Tools (14) Code Quality (13) Search (13) Spark (13) Spring (13) Company - LinkedIn (12) How to (12) Interview-Database (12) Interview-Operating System (12) Solr (12) Architecture Principles (11) Resource (10) Amazon (9) Cache (9) Git (9) Interview - MultiThread (9) Scalability (9) Trouble Shooting (9) Web Dev (9) Architecture Model (8) Better Programmer (8) Cassandra (8) Company - Uber (8) Java67 (8) Math (8) OO Design principles (8) SOLID (8) Design (7) Interview Corner (7) JVM (7) Java Basics (7) Kafka (7) Mac (7) Machine Learning (7) NoSQL (7) C++ (6) Chrome (6) File System (6) Highscalability (6) How to Better (6) Network (6) Restful (6) CareerCup (5) Code Review (5) Hash (5) How to Interview (5) JDK Source Code (5) JavaScript (5) Leetcode (5) Must Known (5) Python (5)

Popular Posts