// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. library dart.pkg.collection.priority_queue; import "dart:collection" show SplayTreeSet; /** * A priority queue is a priority based work-list of elements. * * The queue allows adding elements, and removing them again in priority order. */ abstract class PriorityQueue { /** * Number of elements in the queue. */ int get length; /** * Whether the queue is empty. */ bool get isEmpty; /** * Whether the queue has any elements. */ bool get isNotEmpty; /** * Checks if [object] is in the queue. * * Returns true if the element is found. */ bool contains(E object); /** * Adds element to the queue. * * The element will become the next to be removed by [removeFirst] * when all elements with higher priority have been removed. */ void add(E element); /** * Adds all [elements] to the queue. */ void addAll(Iterable elements); /** * Returns the next element that will be returned by [removeFirst]. * * The element is not removed from the queue. * * The queue must not be empty when this method is called. */ E get first; /** * Removes and returns the element with the highest priority. * * Repeatedly calling this method, without adding element in between, * is guaranteed to return elements in non-decreasing order as, specified by * [comparison]. * * The queue must not be empty when this method is called. */ E removeFirst(); /** * Removes an element that compares equal to [element] in the queue. * * Returns true if an element is found and removed, * and false if no equal element is found. */ bool remove(E element); /** * Removes all the elements from this queue and returns them. * * The returned iterable has no specified order. */ Iterable removeAll(); /** * Removes all the elements from this queue. */ void clear(); /** * Returns a list of the elements of this queue in priority order. * * The queue is not modified. * * The order is the order that the elements would be in if they were * removed from this queue using [removeFirst]. */ List toList(); /** * Return a comparator based set using the comparator of this queue. * * The queue is not modified. * * The returned [Set] is currently a [SplayTreeSet], * but this may change as other ordered sets are implemented. * * The set contains all the elements of this queue. * If an element occurs more than once in the queue, * the set will contain it only once. */ Set toSet(); } /** * Heap based priority queue. * * The elements are kept in a heap structure, * where the element with the highest priority is immediately accessible, * and modifying a single element takes * logarithmic time in the number of elements on average. * * * The [add] and [removeFirst] operations take amortized logarithmic time, * O(log(n)), but may occasionally take linear time when growing the capacity * of the heap. * * The [addAll] operation works as doing repeated [add] operations. * * The [first] getter takes constant time, O(1). * * The [clear] and [removeAll] methods also take constant time, O(1). * * The [contains] and [remove] operations may need to search the entire * queue for the elements, taking O(n) time. * * The [toList] operation effectively sorts the elements, taking O(n*log(n)) * time. * * The [toSet] operation effectively adds each element to the new set, taking * an expected O(n*log(n)) time. */ class HeapPriorityQueue implements PriorityQueue { /** * Initial capacity of a queue when created, or when added to after a [clear]. * * Number can be any positive value. Picking a size that gives a whole * number of "tree levels" in the heap is only done for aesthetic reasons. */ static const int _INITIAL_CAPACITY = 7; /** * The comparison being used to compare the priority of elements. */ final Comparator comparison; /** * List implementation of a heap. */ List _queue = new List(_INITIAL_CAPACITY); /** * Number of elements in queue. * * The heap is implemented in the first [_length] entries of [_queue]. */ int _length = 0; /** * Create a new priority queue. * * The [comparison] is a [Comparator] used to compare the priority of * elements. An element that compares as less than another element has * a higher priority. * * If [comparison] is omitted, it defaults to [Comparable.compare]. */ HeapPriorityQueue([int comparison(E e1, E e2)]) : comparison = (comparison != null) ? comparison : Comparable.compare; void add(E element) { _add(element); } void addAll(Iterable elements) { for (E element in elements) { _add(element); } } void clear() { _queue = const []; _length = 0; } bool contains(E object) { return _locate(object) >= 0; } E get first { if (_length == 0) throw new StateError("No such element"); return _queue[0]; } bool get isEmpty => _length == 0; bool get isNotEmpty => _length != 0; int get length => _length; bool remove(E element) { int index = _locate(element); if (index < 0) return false; E last = _removeLast(); if (index < _length) { int comp = comparison(last, element); if (comp <= 0) { _bubbleUp(last, index); } else { _bubbleDown(last, index); } } return true; } Iterable removeAll() { List result = _queue; int length = _length; _queue = const []; _length = 0; return result.take(length); } E removeFirst() { if (_length == 0) throw new StateError("No such element"); E result = _queue[0]; E last = _removeLast(); if (_length > 0) { _bubbleDown(last, 0); } return result; } List toList() { List list = new List()..length = _length; list.setRange(0, _length, _queue); list.sort(comparison); return list; } Set toSet() { Set set = new SplayTreeSet(comparison); for (int i = 0; i < _length; i++) { set.add(_queue[i]); } return set; } /** * Returns some representation of the queue. * * The format isn't significant, and may change in the future. */ String toString() { return _queue.take(_length).toString(); } /** * Add element to the queue. * * Grows the capacity if the backing list is full. */ void _add(E element) { if (_length == _queue.length) _grow(); _bubbleUp(element, _length++); } /** * Find the index of an object in the heap. * * Returns -1 if the object is not found. */ int _locate(E object) { if (_length == 0) return -1; // Count positions from one instad of zero. This gives the numbers // some nice properties. For example, all right children are odd, // their left sibling is even, and the parent is found by shifting // right by one. // Valid range for position is [1.._length], inclusive. int position = 1; // Pre-order depth first search, omit child nodes if the current // node has lower priority than [object], because all nodes lower // in the heap will also have lower priority. do { int index = position - 1; E element = _queue[index]; int comp = comparison(element, object); if (comp == 0) return index; if (comp < 0) { // Element may be in subtree. // Continue with the left child, if it is there. int leftChildPosition = position * 2; if (leftChildPosition <= _length) { position = leftChildPosition; continue; } } // Find the next right sibling or right ancestor sibling. do { while (position.isOdd) { // While position is a right child, go to the parent. position >>= 1; } // Then go to the right sibling of the left-child. position += 1; } while (position > _length); // Happens if last element is a left child. } while (position != 1); // At root again. Happens for right-most element. return -1; } E _removeLast() { int newLength = _length - 1; E last = _queue[newLength]; _queue[newLength] = null; _length = newLength; return last; } /** * Place [element] in heap at [index] or above. * * Put element into the empty cell at `index`. * While the `element` has higher priority than the * parent, swap it with the parent. */ void _bubbleUp(E element, int index) { while (index > 0) { int parentIndex = (index - 1) ~/ 2; E parent = _queue[parentIndex]; if (comparison(element, parent) > 0) break; _queue[index] = parent; index = parentIndex; } _queue[index] = element; } /** * Place [element] in heap at [index] or above. * * Put element into the empty cell at `index`. * While the `element` has lower priority than either child, * swap it with the highest priority child. */ void _bubbleDown(E element, int index) { int rightChildIndex = index * 2 + 2; while (rightChildIndex < _length) { int leftChildIndex = rightChildIndex - 1; E leftChild = _queue[leftChildIndex]; E rightChild = _queue[rightChildIndex]; int comp = comparison(leftChild, rightChild); int minChildIndex; E minChild; if (comp < 0) { minChild = leftChild; minChildIndex = leftChildIndex; } else { minChild = rightChild; minChildIndex = rightChildIndex; } comp = comparison(element, minChild); if (comp <= 0) { _queue[index] = element; return; } _queue[index] = minChild; index = minChildIndex; rightChildIndex = index * 2 + 2; } int leftChildIndex = rightChildIndex - 1; if (leftChildIndex < _length) { E child = _queue[leftChildIndex]; int comp = comparison(element, child); if (comp > 0) { _queue[index] = child; index = leftChildIndex; } } _queue[index] = element; } /** * Grows the capacity of the list holding the heap. * * Called when the list is full. */ void _grow() { int newCapacity = _queue.length * 2 + 1; if (newCapacity < _INITIAL_CAPACITY) newCapacity = _INITIAL_CAPACITY; List newQueue = new List(newCapacity); newQueue.setRange(0, _length, _queue); _queue = newQueue; } }