Parsing the set type source java PriorityQueue

The second would have wanted to resolve what LinkedList, but after a sweep at the source, that LinkedList is relatively simple to achieve, not what it meant, so the venue PriorityQueue.

PriorityQueue through the array implements a heap data structure (corresponding to a binary tree), priority element can be calculated by a Comparator, Comparator if not specified, then type must implement Comparable interface element. Compare the final results of the smallest element, on the root of the heap.

Member variables

public class PriorityQueue<E>  extends AbstractQueue<E> {
    transient Object[] queue; // non-private to simplify nested class access
    private int size = 0;
    private final Comparator<? super E> comparator;
    transient int modCount = 0; // non-private to simplify nested class access
}

PriorityQueue member variables and ArrayList highly similar:

  • queue storage element array, representative of a complete binary tree, left and right subtrees of the node n index positions were (2n + 1) and (2n + 2);
  • size number of elements
  • modCount queue modification tracking tag
  • comparator priority comparator, may be null

Heap algorithm

The most important part is to maintain several methods PriorityQueue heap, it basically achieved the "Introduction to Algorithms" heap algorithm described.

1, insert elements siftDown
assumed k are positions left and right subtrees stack, the method siftDown element inserted position x k, and then the sub-stacks is adjusted to ensure that k is the subtree rooted stack. Depending on whether there is a comparator, siftDown use siftDownUsingComparator or siftDownComparable, they are just not the way to compare elements, the structure is exactly the same, here only interpretation siftDownComparable.

 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;
}
  • First calculate a half position, because after this position is a leaf node, do not need to continue down;
  • Calculating the left and right child node k, find the minimum value of the compare node;
  • If that is x, then k is the insertion position;
  • If the child node, the child node value shift, k child node points to the location
  • Continue to try to explore down

2, insert elements siftUp

To the position of the insertion value k x, siftUp embodiment uses a direction toward the root, a relatively large switching down the ancestor node compare value.
siftup use a little less.

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;
}
  • If k> 0, then k is not a root, it can continue up to explore;
  • Find the value of k parent parent, and the parent of comparative x
  • If x> = parent, described on x k, to the root is a parent sub-stack;
  • Otherwise, the parent into the position k, k point to parent, to continue to explore

3, built heap

@SuppressWarnings("unchecked")
private void heapify() {
    for (int i = (size >>> 1) - 1; i >= 0; i--)
        siftDown(i, (E) queue[i]);
}

Start (next is a leaf node) index from the middle position, to traverse the tree root, call siftDown for each location.

  • The initial value of i, because only one leaf node, which satisfies the 左右子树都是子堆condition, siftDown such that, in the root node i is also sub-stack;
  • When i becomes smaller, since k> i already subtree stack, then there must i are the left and right subtrees stack, so that siftDown to as the root node i is sub-stack;
  • Final siftDown (0, queue [0]) into a stack such that the whole tree.

offer and poll

Now you can take a look at the two most common queue operations: offer and poll.

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;
}

offer a first object into the end of the queue in front of the inspection and space for growth and ArrayList very similar.
i is the insertion position, if i == 0, queue is empty, insert gets the job done; otherwise inserted through siftUp, because i is a leaf node, so i can be considered sub-tree is a sub-stacks, siftup will go to ensure that e tree root direction, to find a suitable location, so that the characteristics of the whole tree to maintain the heap.

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;
}

root element poll taken as the return value, then the last element taken out, inserted into the root position by the siftDown, said earlier siftDown nature of this operation makes the whole tree remains stack characteristics.

remove operation

public boolean remove(Object o) {
    int i = indexOf(o);
    if (i == -1)
        return false;
    else {
        removeAt(i);
        return true;
    }
}
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;
}

The private operation of removeAt more interesting, it performs the operation is to remove the i-node (i-th storage space, rather than the priority of the i-th, this easily lead to people's misunderstanding, there is no similar public method) .

  • If you delete the last element is directly set to null;
  • Otherwise, the last element taken out, inserted into the position i
  • Insertion process is to siftDown, if the final position siftDown is i, then that move are smaller than the sub-tree element i, this time to try siftUp; otherwise it is not necessary to perform the siftUp;
  • When the result siftUp execution is the last element to be moved to before i, then return to this element, otherwise return null

The return value is a surprise, instead of returning elements removed, but in the process of maintaining the heap property, if there is a position before the trailing elements are moved to i, then return it. This is purely to help PriorityQueue iterator implemented immediately explain in the next section.

Iterator

First of all make it clear that, PriorityQueue iterator is not in order of priority to traverse the elements, mainly by the storage order to traverse, look at the members of the iterator

private final class Itr implements Iterator<E> {
    private int cursor = 0;
    private int lastRet = -1;
    private int expectedModCount = modCount;

    private ArrayDeque<E> forgetMeNot = null;
    private E lastRetElt = null;
}

cursor, exactly the same lastRet, and the role of expectedModCount ArrayList iterator; but more out of forgetMeNot and lastRetElt was a bit Mo wonderful people.

Look at implement the remove method:

public void remove() {
    if (expectedModCount != modCount)
        throw new ConcurrentModificationException();
    if (lastRet != -1) {
        E moved = PriorityQueue.this.removeAt(lastRet);
        lastRet = -1;
        if (moved == null)
            cursor--;
        else {
            if (forgetMeNot == null)
                forgetMeNot = new ArrayDeque<>();
            forgetMeNot.add(moved);
        }
    } else if (lastRetElt != null) {
        PriorityQueue.this.removeEq(lastRetElt);
        lastRetElt = null;
    } else {
        throw new IllegalStateException();
    }
    expectedModCount = modCount;
}

If lastRet valid, then call PriorityQueued.removeAt (lastRet) to delete the elements, by the one we know, removeAt approach may result in an element is moved from the end to the front lastRet, this is the case, the iterator will lose that element. To solve this problem, an iterator to that element into a temporary ArrayDeque inside.

If this does not point to lastRet effective element, it is possible to traverse ArrayDeque elements inside, this time to point through lastRetElt.

Look next method is very easy to understand

public E next() {
    if (expectedModCount != modCount)
        throw new ConcurrentModificationException();
    if (cursor < size)
        return (E) queue[lastRet = cursor++];
    if (forgetMeNot != null) {
        lastRet = -1;
        lastRetElt = forgetMeNot.poll();
        if (lastRetElt != null)
            return lastRetElt;
    }
    throw new NoSuchElementException();
}

First traversal along the cursor, then forgetMeNot elements inside traversed again.

Guess you like

Origin www.cnblogs.com/longhuihu/p/11128406.html