| // 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. |
| |
| import 'dart:collection'; |
| |
| import 'utils.dart'; |
| |
| /// A priority queue is a priority based work-list of elements. |
| /// |
| /// The queue allows adding elements, and removing them again in priority order. |
| /// The same object can be added to the queue more than once. |
| /// There is no specified ordering for objects with the same priority |
| /// (where the `comparison` function returns zero). |
| /// |
| /// Operations which care about object equality, [contains] and [remove], |
| /// use [Object.==] for testing equality. |
| /// In most situations this will be the same as identity ([identical]), |
| /// but there are types, like [String], where users can reasonably expect |
| /// distinct objects to represent the same value. |
| /// If elements override [Object.==], the `comparison` function must |
| /// always give equal objects the same priority, |
| /// otherwise [contains] or [remove] might not work correctly. |
| abstract class PriorityQueue<E> { |
| /// Creates an empty [PriorityQueue]. |
| /// |
| /// The created [PriorityQueue] is a plain [HeapPriorityQueue]. |
| /// |
| /// 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]. If this |
| /// is the case, `E` must implement [Comparable], and this is checked at |
| /// runtime for every comparison. |
| factory PriorityQueue([int Function(E, E)? comparison]) = |
| HeapPriorityQueue<E>; |
| |
| /// 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. |
| /// |
| /// Uses the [Object.==] of elements in the queue to check |
| /// for whether they are equal to [object]. |
| /// Equal objects objects must have the same priority |
| /// according to the [comparison] function. |
| /// That is, if `a == b` then `comparison(a, b) == 0`. |
| /// If that is not the case, this check might fail to find |
| /// an object. |
| 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<E> 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 of the queue that compares equal to [element]. |
| /// |
| /// Returns true if an element is found and removed, |
| /// and false if no equal element is found. |
| /// |
| /// If the queue contains more than one object equal to [element], |
| /// only one of them is removed. |
| /// |
| /// Uses the [Object.==] of elements in the queue to check |
| /// for whether they are equal to [element]. |
| /// Equal objects objects must have the same priority |
| /// according to the [comparison] function. |
| /// That is, if `a == b` then `comparison(a, b) == 0`. |
| /// If that is not the case, this check might fail to find |
| /// an object. |
| bool remove(E element); |
| |
| /// Removes all the elements from this queue and returns them. |
| /// |
| /// The returned iterable has no specified order. |
| Iterable<E> 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<E> toList(); |
| |
| /// Returns a list of the elements of this queue in no specific order. |
| /// |
| /// The queue is not modified. |
| /// |
| /// The order of the elements is implementation specific. |
| /// The order may differ between different calls on the same queue. |
| List<E> toUnorderedList(); |
| |
| /// 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<E> 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 [toUnorderedList] operation copies, but does not sort, the elements, |
| /// and is linear, O(n). |
| /// * The [toSet] operation effectively adds each element to the new set, taking |
| /// an expected O(n*log(n)) time. |
| class HeapPriorityQueue<E> implements PriorityQueue<E> { |
| /// 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<E> comparison; |
| |
| /// List implementation of a heap. |
| List<E?> _queue = List<E?>.filled(_INITIAL_CAPACITY, null); |
| |
| /// 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]. If this |
| /// is the case, `E` must implement [Comparable], and this is checked at |
| /// runtime for every comparison. |
| HeapPriorityQueue([int Function(E, E)? comparison]) |
| : comparison = comparison ?? defaultCompare<E>(); |
| |
| E _elementAt(int index) => _queue[index] ?? (null as E); |
| |
| @override |
| void add(E element) { |
| _add(element); |
| } |
| |
| @override |
| void addAll(Iterable<E> elements) { |
| for (var element in elements) { |
| _add(element); |
| } |
| } |
| |
| @override |
| void clear() { |
| _queue = const []; |
| _length = 0; |
| } |
| |
| @override |
| bool contains(E object) => _locate(object) >= 0; |
| |
| @override |
| E get first { |
| if (_length == 0) throw StateError('No element'); |
| return _elementAt(0); |
| } |
| |
| @override |
| bool get isEmpty => _length == 0; |
| |
| @override |
| bool get isNotEmpty => _length != 0; |
| |
| @override |
| int get length => _length; |
| |
| @override |
| bool remove(E element) { |
| var index = _locate(element); |
| if (index < 0) return false; |
| var last = _removeLast(); |
| if (index < _length) { |
| var comp = comparison(last, element); |
| if (comp <= 0) { |
| _bubbleUp(last, index); |
| } else { |
| _bubbleDown(last, index); |
| } |
| } |
| return true; |
| } |
| |
| @override |
| Iterable<E> removeAll() { |
| var result = _queue; |
| var length = _length; |
| _queue = const []; |
| _length = 0; |
| return result.take(length).cast(); |
| } |
| |
| @override |
| E removeFirst() { |
| if (_length == 0) throw StateError('No element'); |
| var result = _elementAt(0); |
| var last = _removeLast(); |
| if (_length > 0) { |
| _bubbleDown(last, 0); |
| } |
| return result; |
| } |
| |
| @override |
| List<E> toList() => _toUnorderedList()..sort(comparison); |
| |
| @override |
| Set<E> toSet() { |
| var set = SplayTreeSet<E>(comparison); |
| for (var i = 0; i < _length; i++) { |
| set.add(_elementAt(i)); |
| } |
| return set; |
| } |
| |
| @override |
| List<E> toUnorderedList() => _toUnorderedList(); |
| |
| List<E> _toUnorderedList() => |
| [for (var i = 0; i < _length; i++) _elementAt(i)]; |
| |
| /// Returns some representation of the queue. |
| /// |
| /// The format isn't significant, and may change in the future. |
| @override |
| 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. |
| /// |
| /// A matching object, `o`, must satisfy that |
| /// `comparison(o, object) == 0 && o == object`. |
| int _locate(E object) { |
| if (_length == 0) return -1; |
| // Count positions from one instead 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. |
| var 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 { |
| var index = position - 1; |
| var element = _elementAt(index); |
| var comp = comparison(element, object); |
| if (comp <= 0) { |
| if (comp == 0 && element == object) return index; |
| // Element may be in subtree. |
| // Continue with the left child, if it is there. |
| var 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() { |
| var newLength = _length - 1; |
| var last = _elementAt(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) { |
| var parentIndex = (index - 1) ~/ 2; |
| var parent = _elementAt(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) { |
| var rightChildIndex = index * 2 + 2; |
| while (rightChildIndex < _length) { |
| var leftChildIndex = rightChildIndex - 1; |
| var leftChild = _elementAt(leftChildIndex); |
| var rightChild = _elementAt(rightChildIndex); |
| var 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; |
| } |
| var leftChildIndex = rightChildIndex - 1; |
| if (leftChildIndex < _length) { |
| var child = _elementAt(leftChildIndex); |
| var 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() { |
| var newCapacity = _queue.length * 2 + 1; |
| if (newCapacity < _INITIAL_CAPACITY) newCapacity = _INITIAL_CAPACITY; |
| var newQueue = List<E?>.filled(newCapacity, null); |
| newQueue.setRange(0, _length, _queue); |
| _queue = newQueue; |
| } |
| } |