945 lines
29 KiB
Java
945 lines
29 KiB
Java
/*
|
|
* Copyright (C) 2010 The Guava Authors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.google.common.collect;
|
|
|
|
import static com.google.common.base.Preconditions.checkArgument;
|
|
import static com.google.common.base.Preconditions.checkNotNull;
|
|
import static com.google.common.base.Preconditions.checkPositionIndex;
|
|
import static com.google.common.base.Preconditions.checkState;
|
|
import static com.google.common.collect.CollectPreconditions.checkRemove;
|
|
|
|
import java.util.AbstractQueue;
|
|
import java.util.ArrayDeque;
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.Comparator;
|
|
import java.util.ConcurrentModificationException;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.NoSuchElementException;
|
|
import java.util.PriorityQueue;
|
|
import java.util.Queue;
|
|
|
|
import com.google.common.annotations.Beta;
|
|
import com.google.common.annotations.VisibleForTesting;
|
|
import com.google.common.math.IntMath;
|
|
|
|
/**
|
|
* A double-ended priority queue, which provides constant-time access to both
|
|
* its least element and its greatest element, as determined by the queue's
|
|
* specified comparator. If no comparator is given at construction time, the
|
|
* natural order of elements is used.
|
|
*
|
|
* <p>
|
|
* As a {@link Queue} it functions exactly as a {@link PriorityQueue}: its head
|
|
* element -- the implicit target of the methods {@link #peek()},
|
|
* {@link #poll()} and {@link #remove()} -- is defined as the <i>least</i>
|
|
* element in the queue according to the queue's comparator. But unlike a
|
|
* regular priority queue, the methods {@link #peekLast}, {@link #pollLast} and
|
|
* {@link #removeLast} are also provided, to act on the <i>greatest</i> element
|
|
* in the queue instead.
|
|
*
|
|
* <p>
|
|
* A min-max priority queue can be configured with a maximum size. If so, each
|
|
* time the size of the queue exceeds that value, the queue automatically
|
|
* removes its greatest element according to its comparator (which might be the
|
|
* element that was just added). This is different from conventional bounded
|
|
* queues, which either block or reject new elements when full.
|
|
*
|
|
* <p>
|
|
* This implementation is based on the
|
|
* <a href="http://portal.acm.org/citation.cfm?id=6621">min-max heap</a>
|
|
* developed by Atkinson, et al. Unlike many other double-ended priority queues,
|
|
* it stores elements in a single array, as compact as the traditional heap data
|
|
* structure used in {@link PriorityQueue}.
|
|
*
|
|
* <p>
|
|
* This class is not thread-safe, and does not accept null elements.
|
|
*
|
|
* <p>
|
|
* <i>Performance notes:</i>
|
|
*
|
|
* <ul>
|
|
* <li>The retrieval operations {@link #peek}, {@link #peekFirst},
|
|
* {@link #peekLast}, {@link #element}, and {@link #size} are constant-time
|
|
* <li>The enqueing and dequeing operations ({@link #offer}, {@link #add}, and
|
|
* all the forms of {@link #poll} and {@link #remove()}) run in {@code
|
|
* O(log n) time}
|
|
* <li>The {@link #remove(Object)} and {@link #contains} operations require
|
|
* linear ({@code O(n)}) time
|
|
* <li>If you only access one end of the queue, and don't use a maximum size,
|
|
* this class is functionally equivalent to {@link PriorityQueue}, but
|
|
* significantly slower.
|
|
* </ul>
|
|
*
|
|
* @author Sverre Sundsdal
|
|
* @author Torbjorn Gannholm
|
|
* @since 8.0
|
|
*/
|
|
// TODO(kevinb): GWT compatibility
|
|
@Beta
|
|
public final class MinMaxPriorityQueue<E> extends AbstractQueue<E> {
|
|
|
|
/**
|
|
* Creates a new min-max priority queue with default settings: natural order, no
|
|
* maximum size, no initial contents, and an initial expected size of 11.
|
|
*/
|
|
public static <E extends Comparable<E>> MinMaxPriorityQueue<E> create() {
|
|
return new Builder<Comparable>(Ordering.natural()).create();
|
|
}
|
|
|
|
/**
|
|
* Creates a new min-max priority queue using natural order, no maximum size,
|
|
* and initially containing the given elements.
|
|
*/
|
|
public static <E extends Comparable<E>> MinMaxPriorityQueue<E> create(Iterable<? extends E> initialContents) {
|
|
return new Builder<E>(Ordering.<E>natural()).create(initialContents);
|
|
}
|
|
|
|
/**
|
|
* Creates and returns a new builder, configured to build {@code
|
|
* MinMaxPriorityQueue} instances that use {@code comparator} to determine the
|
|
* least and greatest elements.
|
|
*/
|
|
public static <B> Builder<B> orderedBy(Comparator<B> comparator) {
|
|
return new Builder<B>(comparator);
|
|
}
|
|
|
|
/**
|
|
* Creates and returns a new builder, configured to build {@code
|
|
* MinMaxPriorityQueue} instances sized appropriately to hold {@code
|
|
* expectedSize} elements.
|
|
*/
|
|
public static Builder<Comparable> expectedSize(int expectedSize) {
|
|
return new Builder<Comparable>(Ordering.natural()).expectedSize(expectedSize);
|
|
}
|
|
|
|
/**
|
|
* Creates and returns a new builder, configured to build {@code
|
|
* MinMaxPriorityQueue} instances that are limited to {@code maximumSize}
|
|
* elements. Each time a queue grows beyond this bound, it immediately removes
|
|
* its greatest element (according to its comparator), which might be the
|
|
* element that was just added.
|
|
*/
|
|
public static Builder<Comparable> maximumSize(int maximumSize) {
|
|
return new Builder<Comparable>(Ordering.natural()).maximumSize(maximumSize);
|
|
}
|
|
|
|
/**
|
|
* The builder class used in creation of min-max priority queues. Instead of
|
|
* constructing one directly, use
|
|
* {@link MinMaxPriorityQueue#orderedBy(Comparator)},
|
|
* {@link MinMaxPriorityQueue#expectedSize(int)} or
|
|
* {@link MinMaxPriorityQueue#maximumSize(int)}.
|
|
*
|
|
* @param <B> the upper bound on the eventual type that can be produced by this
|
|
* builder (for example, a {@code Builder<Number>} can produce a
|
|
* {@code Queue<Number>} or {@code Queue<Integer>} but not a {@code
|
|
* Queue<Object>}).
|
|
* @since 8.0
|
|
*/
|
|
@Beta
|
|
public static final class Builder<B> {
|
|
/*
|
|
* TODO(kevinb): when the dust settles, see if we still need this or can just
|
|
* default to DEFAULT_CAPACITY.
|
|
*/
|
|
private static final int UNSET_EXPECTED_SIZE = -1;
|
|
|
|
private final Comparator<B> comparator;
|
|
private int expectedSize = UNSET_EXPECTED_SIZE;
|
|
private int maximumSize = Integer.MAX_VALUE;
|
|
|
|
private Builder(Comparator<B> comparator) {
|
|
this.comparator = checkNotNull(comparator);
|
|
}
|
|
|
|
/**
|
|
* Configures this builder to build min-max priority queues with an initial
|
|
* expected size of {@code expectedSize}.
|
|
*/
|
|
public Builder<B> expectedSize(int expectedSize) {
|
|
checkArgument(expectedSize >= 0);
|
|
this.expectedSize = expectedSize;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Configures this builder to build {@code MinMaxPriorityQueue} instances that
|
|
* are limited to {@code maximumSize} elements. Each time a queue grows beyond
|
|
* this bound, it immediately removes its greatest element (according to its
|
|
* comparator), which might be the element that was just added.
|
|
*/
|
|
public Builder<B> maximumSize(int maximumSize) {
|
|
checkArgument(maximumSize > 0);
|
|
this.maximumSize = maximumSize;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Builds a new min-max priority queue using the previously specified options,
|
|
* and having no initial contents.
|
|
*/
|
|
public <T extends B> MinMaxPriorityQueue<T> create() {
|
|
return create(Collections.<T>emptySet());
|
|
}
|
|
|
|
/**
|
|
* Builds a new min-max priority queue using the previously specified options,
|
|
* and having the given initial elements.
|
|
*/
|
|
public <T extends B> MinMaxPriorityQueue<T> create(Iterable<? extends T> initialContents) {
|
|
MinMaxPriorityQueue<T> queue = new MinMaxPriorityQueue<T>(this,
|
|
initialQueueSize(expectedSize, maximumSize, initialContents));
|
|
for (T element : initialContents) {
|
|
queue.offer(element);
|
|
}
|
|
return queue;
|
|
}
|
|
|
|
@SuppressWarnings("unchecked") // safe "contravariant cast"
|
|
private <T extends B> Ordering<T> ordering() {
|
|
return Ordering.from((Comparator<T>) comparator);
|
|
}
|
|
}
|
|
|
|
private final Heap minHeap;
|
|
private final Heap maxHeap;
|
|
@VisibleForTesting
|
|
final int maximumSize;
|
|
private Object[] queue;
|
|
private int size;
|
|
private int modCount;
|
|
|
|
private MinMaxPriorityQueue(Builder<? super E> builder, int queueSize) {
|
|
Ordering<E> ordering = builder.ordering();
|
|
this.minHeap = new Heap(ordering);
|
|
this.maxHeap = new Heap(ordering.reverse());
|
|
minHeap.otherHeap = maxHeap;
|
|
maxHeap.otherHeap = minHeap;
|
|
|
|
this.maximumSize = builder.maximumSize;
|
|
// TODO(kevinb): pad?
|
|
this.queue = new Object[queueSize];
|
|
}
|
|
|
|
@Override
|
|
public int size() {
|
|
return size;
|
|
}
|
|
|
|
/**
|
|
* Adds the given element to this queue. If this queue has a maximum size, after
|
|
* adding {@code element} the queue will automatically evict its greatest
|
|
* element (according to its comparator), which may be {@code
|
|
* element} itself.
|
|
*
|
|
* @return {@code true} always
|
|
*/
|
|
@Override
|
|
public boolean add(E element) {
|
|
offer(element);
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean addAll(Collection<? extends E> newElements) {
|
|
boolean modified = false;
|
|
for (E element : newElements) {
|
|
offer(element);
|
|
modified = true;
|
|
}
|
|
return modified;
|
|
}
|
|
|
|
/**
|
|
* Adds the given element to this queue. If this queue has a maximum size, after
|
|
* adding {@code element} the queue will automatically evict its greatest
|
|
* element (according to its comparator), which may be {@code
|
|
* element} itself.
|
|
*/
|
|
@Override
|
|
public boolean offer(E element) {
|
|
checkNotNull(element);
|
|
modCount++;
|
|
int insertIndex = size++;
|
|
|
|
growIfNeeded();
|
|
|
|
// Adds the element to the end of the heap and bubbles it up to the correct
|
|
// position.
|
|
heapForIndex(insertIndex).bubbleUp(insertIndex, element);
|
|
return size <= maximumSize || pollLast() != element;
|
|
}
|
|
|
|
@Override
|
|
public E poll() {
|
|
return isEmpty() ? null : removeAndGet(0);
|
|
}
|
|
|
|
@SuppressWarnings("unchecked") // we must carefully only allow Es to get in
|
|
E elementData(int index) {
|
|
return (E) queue[index];
|
|
}
|
|
|
|
@Override
|
|
public E peek() {
|
|
return isEmpty() ? null : elementData(0);
|
|
}
|
|
|
|
/**
|
|
* Returns the index of the max element.
|
|
*/
|
|
private int getMaxElementIndex() {
|
|
switch (size) {
|
|
case 1:
|
|
return 0; // The lone element in the queue is the maximum.
|
|
case 2:
|
|
return 1; // The lone element in the maxHeap is the maximum.
|
|
default:
|
|
// The max element must sit on the first level of the maxHeap. It is
|
|
// actually the *lesser* of the two from the maxHeap's perspective.
|
|
return (maxHeap.compareElements(1, 2) <= 0) ? 1 : 2;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes and returns the least element of this queue, or returns {@code
|
|
* null} if the queue is empty.
|
|
*/
|
|
public E pollFirst() {
|
|
return poll();
|
|
}
|
|
|
|
/**
|
|
* Removes and returns the least element of this queue.
|
|
*
|
|
* @throws NoSuchElementException if the queue is empty
|
|
*/
|
|
public E removeFirst() {
|
|
return remove();
|
|
}
|
|
|
|
/**
|
|
* Retrieves, but does not remove, the least element of this queue, or returns
|
|
* {@code null} if the queue is empty.
|
|
*/
|
|
public E peekFirst() {
|
|
return peek();
|
|
}
|
|
|
|
/**
|
|
* Removes and returns the greatest element of this queue, or returns {@code
|
|
* null} if the queue is empty.
|
|
*/
|
|
public E pollLast() {
|
|
return isEmpty() ? null : removeAndGet(getMaxElementIndex());
|
|
}
|
|
|
|
/**
|
|
* Removes and returns the greatest element of this queue.
|
|
*
|
|
* @throws NoSuchElementException if the queue is empty
|
|
*/
|
|
public E removeLast() {
|
|
if (isEmpty()) {
|
|
throw new NoSuchElementException();
|
|
}
|
|
return removeAndGet(getMaxElementIndex());
|
|
}
|
|
|
|
/**
|
|
* Retrieves, but does not remove, the greatest element of this queue, or
|
|
* returns {@code null} if the queue is empty.
|
|
*/
|
|
public E peekLast() {
|
|
return isEmpty() ? null : elementData(getMaxElementIndex());
|
|
}
|
|
|
|
/**
|
|
* Removes the element at position {@code index}.
|
|
*
|
|
* <p>
|
|
* Normally this method leaves the elements at up to {@code index - 1},
|
|
* inclusive, untouched. Under these circumstances, it returns {@code null}.
|
|
*
|
|
* <p>
|
|
* Occasionally, in order to maintain the heap invariant, it must swap a later
|
|
* element of the list with one before {@code index}. Under these circumstances
|
|
* it returns a pair of elements as a {@link MoveDesc}. The first one is the
|
|
* element that was previously at the end of the heap and is now at some
|
|
* position before {@code index}. The second element is the one that was swapped
|
|
* down to replace the element at {@code index}. This fact is used by
|
|
* iterator.remove so as to visit elements during a traversal once and only
|
|
* once.
|
|
*/
|
|
@VisibleForTesting
|
|
MoveDesc<E> removeAt(int index) {
|
|
checkPositionIndex(index, size);
|
|
modCount++;
|
|
size--;
|
|
if (size == index) {
|
|
queue[size] = null;
|
|
return null;
|
|
}
|
|
E actualLastElement = elementData(size);
|
|
int lastElementAt = heapForIndex(size).getCorrectLastElement(actualLastElement);
|
|
E toTrickle = elementData(size);
|
|
queue[size] = null;
|
|
MoveDesc<E> changes = fillHole(index, toTrickle);
|
|
if (lastElementAt < index) {
|
|
// Last element is moved to before index, swapped with trickled element.
|
|
if (changes == null) {
|
|
// The trickled element is still after index.
|
|
return new MoveDesc<E>(actualLastElement, toTrickle);
|
|
} else {
|
|
// The trickled element is back before index, but the replaced element
|
|
// has now been moved after index.
|
|
return new MoveDesc<E>(actualLastElement, changes.replaced);
|
|
}
|
|
}
|
|
// Trickled element was after index to begin with, no adjustment needed.
|
|
return changes;
|
|
}
|
|
|
|
private MoveDesc<E> fillHole(int index, E toTrickle) {
|
|
Heap heap = heapForIndex(index);
|
|
// We consider elementData(index) a "hole", and we want to fill it
|
|
// with the last element of the heap, toTrickle.
|
|
// Since the last element of the heap is from the bottom level, we
|
|
// optimistically fill index position with elements from lower levels,
|
|
// moving the hole down. In most cases this reduces the number of
|
|
// comparisons with toTrickle, but in some cases we will need to bubble it
|
|
// all the way up again.
|
|
int vacated = heap.fillHoleAt(index);
|
|
// Try to see if toTrickle can be bubbled up min levels.
|
|
int bubbledTo = heap.bubbleUpAlternatingLevels(vacated, toTrickle);
|
|
if (bubbledTo == vacated) {
|
|
// Could not bubble toTrickle up min levels, try moving
|
|
// it from min level to max level (or max to min level) and bubble up
|
|
// there.
|
|
return heap.tryCrossOverAndBubbleUp(index, vacated, toTrickle);
|
|
} else {
|
|
return (bubbledTo < index) ? new MoveDesc<E>(toTrickle, elementData(index)) : null;
|
|
}
|
|
}
|
|
|
|
// Returned from removeAt() to iterator.remove()
|
|
static class MoveDesc<E> {
|
|
final E toTrickle;
|
|
final E replaced;
|
|
|
|
MoveDesc(E toTrickle, E replaced) {
|
|
this.toTrickle = toTrickle;
|
|
this.replaced = replaced;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes and returns the value at {@code index}.
|
|
*/
|
|
private E removeAndGet(int index) {
|
|
E value = elementData(index);
|
|
removeAt(index);
|
|
return value;
|
|
}
|
|
|
|
private Heap heapForIndex(int i) {
|
|
return isEvenLevel(i) ? minHeap : maxHeap;
|
|
}
|
|
|
|
private static final int EVEN_POWERS_OF_TWO = 0x55555555;
|
|
private static final int ODD_POWERS_OF_TWO = 0xaaaaaaaa;
|
|
|
|
@VisibleForTesting
|
|
static boolean isEvenLevel(int index) {
|
|
int oneBased = index + 1;
|
|
checkState(oneBased > 0, "negative index");
|
|
return (oneBased & EVEN_POWERS_OF_TWO) > (oneBased & ODD_POWERS_OF_TWO);
|
|
}
|
|
|
|
/**
|
|
* Returns {@code true} if the MinMax heap structure holds. This is only used in
|
|
* testing.
|
|
*
|
|
* TODO(kevinb): move to the test class?
|
|
*/
|
|
@VisibleForTesting
|
|
boolean isIntact() {
|
|
for (int i = 1; i < size; i++) {
|
|
if (!heapForIndex(i).verifyIndex(i)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Each instance of MinMaxPriortyQueue encapsulates two instances of Heap: a
|
|
* min-heap and a max-heap. Conceptually, these might each have their own array
|
|
* for storage, but for efficiency's sake they are stored interleaved on
|
|
* alternate heap levels in the same array (MMPQ.queue).
|
|
*/
|
|
private class Heap {
|
|
final Ordering<E> ordering;
|
|
Heap otherHeap;
|
|
|
|
Heap(Ordering<E> ordering) {
|
|
this.ordering = ordering;
|
|
}
|
|
|
|
int compareElements(int a, int b) {
|
|
return ordering.compare(elementData(a), elementData(b));
|
|
}
|
|
|
|
/**
|
|
* Tries to move {@code toTrickle} from a min to a max level and bubble up
|
|
* there. If it moved before {@code removeIndex} this method returns a pair as
|
|
* described in {@link #removeAt}.
|
|
*/
|
|
MoveDesc<E> tryCrossOverAndBubbleUp(int removeIndex, int vacated, E toTrickle) {
|
|
int crossOver = crossOver(vacated, toTrickle);
|
|
if (crossOver == vacated) {
|
|
return null;
|
|
}
|
|
// Successfully crossed over from min to max.
|
|
// Bubble up max levels.
|
|
E parent;
|
|
// If toTrickle is moved up to a parent of removeIndex, the parent is
|
|
// placed in removeIndex position. We must return that to the iterator so
|
|
// that it knows to skip it.
|
|
if (crossOver < removeIndex) {
|
|
// We crossed over to the parent level in crossOver, so the parent
|
|
// has already been moved.
|
|
parent = elementData(removeIndex);
|
|
} else {
|
|
parent = elementData(getParentIndex(removeIndex));
|
|
}
|
|
// bubble it up the opposite heap
|
|
if (otherHeap.bubbleUpAlternatingLevels(crossOver, toTrickle) < removeIndex) {
|
|
return new MoveDesc<E>(toTrickle, parent);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Bubbles a value from {@code index} up the appropriate heap if required.
|
|
*/
|
|
void bubbleUp(int index, E x) {
|
|
int crossOver = crossOverUp(index, x);
|
|
|
|
Heap heap;
|
|
if (crossOver == index) {
|
|
heap = this;
|
|
} else {
|
|
index = crossOver;
|
|
heap = otherHeap;
|
|
}
|
|
heap.bubbleUpAlternatingLevels(index, x);
|
|
}
|
|
|
|
/**
|
|
* Bubbles a value from {@code index} up the levels of this heap, and returns
|
|
* the index the element ended up at.
|
|
*/
|
|
int bubbleUpAlternatingLevels(int index, E x) {
|
|
while (index > 2) {
|
|
int grandParentIndex = getGrandparentIndex(index);
|
|
E e = elementData(grandParentIndex);
|
|
if (ordering.compare(e, x) <= 0) {
|
|
break;
|
|
}
|
|
queue[index] = e;
|
|
index = grandParentIndex;
|
|
}
|
|
queue[index] = x;
|
|
return index;
|
|
}
|
|
|
|
/**
|
|
* Returns the index of minimum value between {@code index} and
|
|
* {@code index + len}, or {@code -1} if {@code index} is greater than
|
|
* {@code size}.
|
|
*/
|
|
int findMin(int index, int len) {
|
|
if (index >= size) {
|
|
return -1;
|
|
}
|
|
checkState(index > 0);
|
|
int limit = Math.min(index, size - len) + len;
|
|
int minIndex = index;
|
|
for (int i = index + 1; i < limit; i++) {
|
|
if (compareElements(i, minIndex) < 0) {
|
|
minIndex = i;
|
|
}
|
|
}
|
|
return minIndex;
|
|
}
|
|
|
|
/**
|
|
* Returns the minimum child or {@code -1} if no child exists.
|
|
*/
|
|
int findMinChild(int index) {
|
|
return findMin(getLeftChildIndex(index), 2);
|
|
}
|
|
|
|
/**
|
|
* Returns the minimum grand child or -1 if no grand child exists.
|
|
*/
|
|
int findMinGrandChild(int index) {
|
|
int leftChildIndex = getLeftChildIndex(index);
|
|
if (leftChildIndex < 0) {
|
|
return -1;
|
|
}
|
|
return findMin(getLeftChildIndex(leftChildIndex), 4);
|
|
}
|
|
|
|
/**
|
|
* Moves an element one level up from a min level to a max level (or vice
|
|
* versa). Returns the new position of the element.
|
|
*/
|
|
int crossOverUp(int index, E x) {
|
|
if (index == 0) {
|
|
queue[0] = x;
|
|
return 0;
|
|
}
|
|
int parentIndex = getParentIndex(index);
|
|
E parentElement = elementData(parentIndex);
|
|
if (parentIndex != 0) {
|
|
// This is a guard for the case of the childless uncle.
|
|
// Since the end of the array is actually the middle of the heap,
|
|
// a smaller childless uncle can become a child of x when we
|
|
// bubble up alternate levels, violating the invariant.
|
|
int grandparentIndex = getParentIndex(parentIndex);
|
|
int uncleIndex = getRightChildIndex(grandparentIndex);
|
|
if (uncleIndex != parentIndex && getLeftChildIndex(uncleIndex) >= size) {
|
|
E uncleElement = elementData(uncleIndex);
|
|
if (ordering.compare(uncleElement, parentElement) < 0) {
|
|
parentIndex = uncleIndex;
|
|
parentElement = uncleElement;
|
|
}
|
|
}
|
|
}
|
|
if (ordering.compare(parentElement, x) < 0) {
|
|
queue[index] = parentElement;
|
|
queue[parentIndex] = x;
|
|
return parentIndex;
|
|
}
|
|
queue[index] = x;
|
|
return index;
|
|
}
|
|
|
|
/**
|
|
* Returns the conceptually correct last element of the heap.
|
|
*
|
|
* <p>
|
|
* Since the last element of the array is actually in the middle of the sorted
|
|
* structure, a childless uncle node could be smaller, which would corrupt the
|
|
* invariant if this element becomes the new parent of the uncle. In that case,
|
|
* we first switch the last element with its uncle, before returning.
|
|
*/
|
|
int getCorrectLastElement(E actualLastElement) {
|
|
int parentIndex = getParentIndex(size);
|
|
if (parentIndex != 0) {
|
|
int grandparentIndex = getParentIndex(parentIndex);
|
|
int uncleIndex = getRightChildIndex(grandparentIndex);
|
|
if (uncleIndex != parentIndex && getLeftChildIndex(uncleIndex) >= size) {
|
|
E uncleElement = elementData(uncleIndex);
|
|
if (ordering.compare(uncleElement, actualLastElement) < 0) {
|
|
queue[uncleIndex] = actualLastElement;
|
|
queue[size] = uncleElement;
|
|
return uncleIndex;
|
|
}
|
|
}
|
|
}
|
|
return size;
|
|
}
|
|
|
|
/**
|
|
* Crosses an element over to the opposite heap by moving it one level down (or
|
|
* up if there are no elements below it).
|
|
*
|
|
* Returns the new position of the element.
|
|
*/
|
|
int crossOver(int index, E x) {
|
|
int minChildIndex = findMinChild(index);
|
|
// TODO(kevinb): split the && into two if's and move crossOverUp so it's
|
|
// only called when there's no child.
|
|
if ((minChildIndex > 0) && (ordering.compare(elementData(minChildIndex), x) < 0)) {
|
|
queue[index] = elementData(minChildIndex);
|
|
queue[minChildIndex] = x;
|
|
return minChildIndex;
|
|
}
|
|
return crossOverUp(index, x);
|
|
}
|
|
|
|
/**
|
|
* Fills the hole at {@code index} by moving in the least of its grandchildren
|
|
* to this position, then recursively filling the new hole created.
|
|
*
|
|
* @return the position of the new hole (where the lowest grandchild moved from,
|
|
* that had no grandchild to replace it)
|
|
*/
|
|
int fillHoleAt(int index) {
|
|
int minGrandchildIndex;
|
|
while ((minGrandchildIndex = findMinGrandChild(index)) > 0) {
|
|
queue[index] = elementData(minGrandchildIndex);
|
|
index = minGrandchildIndex;
|
|
}
|
|
return index;
|
|
}
|
|
|
|
private boolean verifyIndex(int i) {
|
|
if ((getLeftChildIndex(i) < size) && (compareElements(i, getLeftChildIndex(i)) > 0)) {
|
|
return false;
|
|
}
|
|
if ((getRightChildIndex(i) < size) && (compareElements(i, getRightChildIndex(i)) > 0)) {
|
|
return false;
|
|
}
|
|
if ((i > 0) && (compareElements(i, getParentIndex(i)) > 0)) {
|
|
return false;
|
|
}
|
|
if ((i > 2) && (compareElements(getGrandparentIndex(i), i) > 0)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// These would be static if inner classes could have static members.
|
|
|
|
private int getLeftChildIndex(int i) {
|
|
return i * 2 + 1;
|
|
}
|
|
|
|
private int getRightChildIndex(int i) {
|
|
return i * 2 + 2;
|
|
}
|
|
|
|
private int getParentIndex(int i) {
|
|
return (i - 1) / 2;
|
|
}
|
|
|
|
private int getGrandparentIndex(int i) {
|
|
return getParentIndex(getParentIndex(i)); // (i - 3) / 4
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Iterates the elements of the queue in no particular order.
|
|
*
|
|
* If the underlying queue is modified during iteration an exception will be
|
|
* thrown.
|
|
*/
|
|
private class QueueIterator implements Iterator<E> {
|
|
private int cursor = -1;
|
|
private int expectedModCount = modCount;
|
|
private Queue<E> forgetMeNot;
|
|
private List<E> skipMe;
|
|
private E lastFromForgetMeNot;
|
|
private boolean canRemove;
|
|
|
|
@Override
|
|
public boolean hasNext() {
|
|
checkModCount();
|
|
return (nextNotInSkipMe(cursor + 1) < size()) || ((forgetMeNot != null) && !forgetMeNot.isEmpty());
|
|
}
|
|
|
|
@Override
|
|
public E next() {
|
|
checkModCount();
|
|
int tempCursor = nextNotInSkipMe(cursor + 1);
|
|
if (tempCursor < size()) {
|
|
cursor = tempCursor;
|
|
canRemove = true;
|
|
return elementData(cursor);
|
|
} else if (forgetMeNot != null) {
|
|
cursor = size();
|
|
lastFromForgetMeNot = forgetMeNot.poll();
|
|
if (lastFromForgetMeNot != null) {
|
|
canRemove = true;
|
|
return lastFromForgetMeNot;
|
|
}
|
|
}
|
|
throw new NoSuchElementException("iterator moved past last element in queue.");
|
|
}
|
|
|
|
@Override
|
|
public void remove() {
|
|
checkRemove(canRemove);
|
|
checkModCount();
|
|
canRemove = false;
|
|
expectedModCount++;
|
|
if (cursor < size()) {
|
|
MoveDesc<E> moved = removeAt(cursor);
|
|
if (moved != null) {
|
|
if (forgetMeNot == null) {
|
|
forgetMeNot = new ArrayDeque<E>();
|
|
skipMe = new ArrayList<E>(3);
|
|
}
|
|
forgetMeNot.add(moved.toTrickle);
|
|
skipMe.add(moved.replaced);
|
|
}
|
|
cursor--;
|
|
} else { // we must have set lastFromForgetMeNot in next()
|
|
checkState(removeExact(lastFromForgetMeNot));
|
|
lastFromForgetMeNot = null;
|
|
}
|
|
}
|
|
|
|
// Finds only this exact instance, not others that are equals()
|
|
private boolean containsExact(Iterable<E> elements, E target) {
|
|
for (E element : elements) {
|
|
if (element == target) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Removes only this exact instance, not others that are equals()
|
|
boolean removeExact(Object target) {
|
|
for (int i = 0; i < size; i++) {
|
|
if (queue[i] == target) {
|
|
removeAt(i);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void checkModCount() {
|
|
if (modCount != expectedModCount) {
|
|
throw new ConcurrentModificationException();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the index of the first element after {@code c} that is not in
|
|
* {@code skipMe} and returns {@code size()} if there is no such element.
|
|
*/
|
|
private int nextNotInSkipMe(int c) {
|
|
if (skipMe != null) {
|
|
while (c < size() && containsExact(skipMe, elementData(c))) {
|
|
c++;
|
|
}
|
|
}
|
|
return c;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns an iterator over the elements contained in this collection, <i>in no
|
|
* particular order</i>.
|
|
*
|
|
* <p>
|
|
* The iterator is <i>fail-fast</i>: If the MinMaxPriorityQueue is modified at
|
|
* any time after the iterator is created, in any way except through the
|
|
* iterator's own remove method, the iterator will generally throw a
|
|
* {@link ConcurrentModificationException}. Thus, in the face of concurrent
|
|
* modification, the iterator fails quickly and cleanly, rather than risking
|
|
* arbitrary, non-deterministic behavior at an undetermined time in the future.
|
|
*
|
|
* <p>
|
|
* Note that the fail-fast behavior of an iterator cannot be guaranteed as it
|
|
* is, generally speaking, impossible to make any hard guarantees in the
|
|
* presence of unsynchronized concurrent modification. Fail-fast iterators throw
|
|
* {@code ConcurrentModificationException} on a best-effort basis. Therefore, it
|
|
* would be wrong to write a program that depended on this exception for its
|
|
* correctness: <i>the fail-fast behavior of iterators should be used only to
|
|
* detect bugs.</i>
|
|
*
|
|
* @return an iterator over the elements contained in this collection
|
|
*/
|
|
@Override
|
|
public Iterator<E> iterator() {
|
|
return new QueueIterator();
|
|
}
|
|
|
|
@Override
|
|
public void clear() {
|
|
for (int i = 0; i < size; i++) {
|
|
queue[i] = null;
|
|
}
|
|
size = 0;
|
|
}
|
|
|
|
@Override
|
|
public Object[] toArray() {
|
|
Object[] copyTo = new Object[size];
|
|
System.arraycopy(queue, 0, copyTo, 0, size);
|
|
return copyTo;
|
|
}
|
|
|
|
/**
|
|
* Returns the comparator used to order the elements in this queue. Obeys the
|
|
* general contract of {@link PriorityQueue#comparator}, but returns
|
|
* {@link Ordering#natural} instead of {@code null} to indicate natural
|
|
* ordering.
|
|
*/
|
|
public Comparator<? super E> comparator() {
|
|
return minHeap.ordering;
|
|
}
|
|
|
|
@VisibleForTesting
|
|
int capacity() {
|
|
return queue.length;
|
|
}
|
|
|
|
// Size/capacity-related methods
|
|
|
|
private static final int DEFAULT_CAPACITY = 11;
|
|
|
|
@VisibleForTesting
|
|
static int initialQueueSize(int configuredExpectedSize, int maximumSize, Iterable<?> initialContents) {
|
|
// Start with what they said, if they said it, otherwise DEFAULT_CAPACITY
|
|
int result = (configuredExpectedSize == Builder.UNSET_EXPECTED_SIZE) ? DEFAULT_CAPACITY
|
|
: configuredExpectedSize;
|
|
|
|
// Enlarge to contain initial contents
|
|
if (initialContents instanceof Collection) {
|
|
int initialSize = ((Collection<?>) initialContents).size();
|
|
result = Math.max(result, initialSize);
|
|
}
|
|
|
|
// Now cap it at maxSize + 1
|
|
return capAtMaximumSize(result, maximumSize);
|
|
}
|
|
|
|
private void growIfNeeded() {
|
|
if (size > queue.length) {
|
|
int newCapacity = calculateNewCapacity();
|
|
Object[] newQueue = new Object[newCapacity];
|
|
System.arraycopy(queue, 0, newQueue, 0, queue.length);
|
|
queue = newQueue;
|
|
}
|
|
}
|
|
|
|
/** Returns ~2x the old capacity if small; ~1.5x otherwise. */
|
|
private int calculateNewCapacity() {
|
|
int oldCapacity = queue.length;
|
|
int newCapacity = (oldCapacity < 64) ? (oldCapacity + 1) * 2 : IntMath.checkedMultiply(oldCapacity / 2, 3);
|
|
return capAtMaximumSize(newCapacity, maximumSize);
|
|
}
|
|
|
|
/** There's no reason for the queueSize to ever be more than maxSize + 1 */
|
|
private static int capAtMaximumSize(int queueSize, int maximumSize) {
|
|
return Math.min(queueSize - 1, maximumSize) + 1; // don't overflow
|
|
}
|
|
}
|