@@ -36,6 +36,19 @@ abstract class PriorityQueue<E> {
3636 factory PriorityQueue ([int Function (E , E )? comparison]) =
3737 HeapPriorityQueue <E >;
3838
39+ /// Create a new priority queue.
40+ ///
41+ /// The [comparison] is a [Comparator] used to compare the priority of
42+ /// elements. An element that compares as less than another element has
43+ /// a higher priority.
44+ ///
45+ /// If [comparison] is omitted, it defaults to [Comparable.compare] . If this
46+ /// is the case, `E` must implement [Comparable] , and this is checked at
47+ /// runtime for every comparison.
48+ factory PriorityQueue .of (
49+ Iterable <E > elements, int Function (E , E ) comparison) =
50+ HeapPriorityQueue <E >.of;
51+
3952 /// Number of elements in the queue.
4053 int get length;
4154
@@ -169,27 +182,16 @@ abstract class PriorityQueue<E> {
169182/// * The [toSet] operation effectively adds each element to the new set, taking
170183/// an expected O(n*log(n)) time.
171184class HeapPriorityQueue <E > implements PriorityQueue <E > {
172- /// Initial capacity of a queue when created, or when added to after a
173- /// [clear] .
174- ///
175- /// Number can be any positive value. Picking a size that gives a whole
176- /// number of "tree levels" in the heap is only done for aesthetic reasons.
177- static const int _initialCapacity = 7 ;
178-
179185 /// The comparison being used to compare the priority of elements.
180186 final Comparator <E > comparison;
181187
182188 /// List implementation of a heap.
183- List <E ?> _queue = List <E ?>.filled (_initialCapacity, null );
184-
185- /// Number of elements in queue.
186- ///
187- /// The heap is implemented in the first [_length] entries of [_queue] .
188- int _length = 0 ;
189+ List <E > _queue;
189190
190191 /// Modification count.
191192 ///
192193 /// Used to detect concurrent modifications during iteration.
194+ /// Incremented whenever an element is added or removed.
193195 int _modificationCount = 0 ;
194196
195197 /// Create a new priority queue.
@@ -202,31 +204,72 @@ class HeapPriorityQueue<E> implements PriorityQueue<E> {
202204 /// is the case, `E` must implement [Comparable] , and this is checked at
203205 /// runtime for every comparison.
204206 HeapPriorityQueue ([int Function (E , E )? comparison])
205- : comparison = comparison ?? defaultCompare;
207+ : comparison = comparison ?? defaultCompare,
208+ _queue = < E > [];
209+
210+ /// Creates a new priority queue containing [elements] .
211+ ///
212+ /// The [comparison] is a [Comparator] used to compare the priority of
213+ /// elements. An element that compares as less than another element has
214+ /// a higher priority.
215+ HeapPriorityQueue .of (Iterable <E > elements, int Function (E , E ) this .comparison)
216+ : _queue = elements.toList () {
217+ _heapify ();
218+ }
206219
207- E _elementAt (int index) => _queue[index] ?? (null as E );
220+ /// Converts an unordered list of elements to a heap-ordered list of elements.
221+ ///
222+ /// Does so by ordering sub-trees iteratively, then bubbling their parent
223+ /// down into the two ordered subtrees.
224+ /// Trivially ignores the last half of elements, which have no children.
225+ /// Does a number of bubble-down steps that is bounded by the number
226+ /// of elements. Each bubble-down step does two comparisons.
227+ void _heapify () {
228+ // Last non-leaf node's index, negative for empty or one-element queue.
229+ var cursor = _queue.length ~ / 2 - 1 ;
230+ while (cursor >= 0 ) {
231+ _bubbleDown (_queue[cursor], cursor);
232+ cursor -= 1 ;
233+ }
234+ }
208235
209236 @override
210237 void add (E element) {
211238 _modificationCount++ ;
212- _add (element);
239+ _queue.add (element);
240+ _bubbleUp (element, _queue.length - 1 );
213241 }
214242
215243 @override
216244 void addAll (Iterable <E > elements) {
217- var modified = 0 ;
218- for (var element in elements) {
219- modified = 1 ;
220- _add (element);
245+ var endIndex = _queue.length;
246+ _queue.addAll (elements);
247+ var newLength = _queue.length;
248+ var addedCount = newLength - endIndex;
249+ if (addedCount == 0 ) return ;
250+ _modificationCount++ ;
251+ // Approximation for when the time to bubble up all added elements,
252+ // taking approx. addedCount * (log2(newLength)-1) comparisons worst-case,
253+ // (bubble-up does one comparison per element per level),
254+ // is slower than just heapifying the entire heap, which does
255+ // newLength * 2 comparisons worst-case.
256+ // Uses `endIndex.bitLength` instead of `newLength.bitLength` because
257+ // if `addedCount` is greater than `newLength`, the bitLength won't matter
258+ // for any non-trivial heap, and if not, every added element is a leaf
259+ // element, so it only has to look at log2(endIndex) parents.
260+ if (addedCount * endIndex.bitLength >= newLength * 2 ) {
261+ _heapify ();
262+ return ;
263+ }
264+ for (var i = endIndex; i < newLength; i++ ) {
265+ _bubbleUp (_queue[i], i);
221266 }
222- _modificationCount += modified;
223267 }
224268
225269 @override
226270 void clear () {
227271 _modificationCount++ ;
228- _queue = const [];
229- _length = 0 ;
272+ _queue.clear ();
230273 }
231274
232275 @override
@@ -243,27 +286,24 @@ class HeapPriorityQueue<E> implements PriorityQueue<E> {
243286 Iterable <E > get unorderedElements => _UnorderedElementsIterable <E >(this );
244287
245288 @override
246- E get first {
247- if (_length == 0 ) throw StateError ('No element' );
248- return _elementAt (0 );
249- }
289+ E get first => _queue.first;
250290
251291 @override
252- bool get isEmpty => _length == 0 ;
292+ bool get isEmpty => _queue.isEmpty ;
253293
254294 @override
255- bool get isNotEmpty => _length != 0 ;
295+ bool get isNotEmpty => _queue.isNotEmpty ;
256296
257297 @override
258- int get length => _length ;
298+ int get length => _queue.length ;
259299
260300 @override
261301 bool remove (E element) {
262302 var index = _locate (element);
263303 if (index < 0 ) return false ;
264304 _modificationCount++ ;
265- var last = _removeLast ();
266- if (index < _length ) {
305+ var last = _queue. removeLast ();
306+ if (index < _queue.length ) {
267307 var comp = comparison (last, element);
268308 if (comp <= 0 ) {
269309 _bubbleUp (last, index);
@@ -284,19 +324,17 @@ class HeapPriorityQueue<E> implements PriorityQueue<E> {
284324 Iterable <E > removeAll () {
285325 _modificationCount++ ;
286326 var result = _queue;
287- var length = _length;
288- _queue = const [];
289- _length = 0 ;
290- return result.take (length).cast ();
327+ _queue = < E > [];
328+ return result.skip (0 ); // Hide list nature.
291329 }
292330
293331 @override
294332 E removeFirst () {
295- if (_length == 0 ) throw StateError ('No element' );
333+ if (_queue.isEmpty ) throw StateError ('No element' );
296334 _modificationCount++ ;
297- var result = _elementAt ( 0 ) ;
298- var last = _removeLast ();
299- if (_length > 0 ) {
335+ var result = _queue.first ;
336+ var last = _queue. removeLast ();
337+ if (_queue.length > 0 ) {
300338 _bubbleDown (last, 0 );
301339 }
302340 return result;
@@ -306,34 +344,19 @@ class HeapPriorityQueue<E> implements PriorityQueue<E> {
306344 List <E > toList () => _toUnorderedList ()..sort (comparison);
307345
308346 @override
309- Set <E > toSet () {
310- var set = SplayTreeSet <E >(comparison);
311- for (var i = 0 ; i < _length; i++ ) {
312- set .add (_elementAt (i));
313- }
314- return set ;
315- }
347+ Set <E > toSet () => SplayTreeSet <E >(comparison)..addAll (_queue);
316348
317349 @override
318350 List <E > toUnorderedList () => _toUnorderedList ();
319351
320- List <E > _toUnorderedList () =>
321- [for (var i = 0 ; i < _length; i++ ) _elementAt (i)];
352+ List <E > _toUnorderedList () => _queue.toList ();
322353
323354 /// Returns some representation of the queue.
324355 ///
325356 /// The format isn't significant, and may change in the future.
326357 @override
327358 String toString () {
328- return _queue.take (_length).toString ();
329- }
330-
331- /// Add element to the queue.
332- ///
333- /// Grows the capacity if the backing list is full.
334- void _add (E element) {
335- if (_length == _queue.length) _grow ();
336- _bubbleUp (element, _length++ );
359+ return _queue.skip (0 ).toString ();
337360 }
338361
339362 /// Find the index of an object in the heap.
@@ -343,7 +366,7 @@ class HeapPriorityQueue<E> implements PriorityQueue<E> {
343366 /// A matching object, `o` , must satisfy that
344367 /// `comparison(o, object) == 0 && o == object` .
345368 int _locate (E object) {
346- if (_length == 0 ) return - 1 ;
369+ if (_queue.isEmpty ) return - 1 ;
347370 // Count positions from one instead of zero. This gives the numbers
348371 // some nice properties. For example, all right children are odd,
349372 // their left sibling is even, and the parent is found by shifting
@@ -355,14 +378,14 @@ class HeapPriorityQueue<E> implements PriorityQueue<E> {
355378 // in the heap will also have lower priority.
356379 do {
357380 var index = position - 1 ;
358- var element = _elementAt ( index) ;
381+ var element = _queue[ index] ;
359382 var comp = comparison (element, object);
360383 if (comp <= 0 ) {
361384 if (comp == 0 && element == object) return index;
362385 // Element may be in subtree.
363386 // Continue with the left child, if it is there.
364387 var leftChildPosition = position * 2 ;
365- if (leftChildPosition <= _length ) {
388+ if (leftChildPosition <= _queue.length ) {
366389 position = leftChildPosition;
367390 continue ;
368391 }
@@ -375,18 +398,13 @@ class HeapPriorityQueue<E> implements PriorityQueue<E> {
375398 }
376399 // Then go to the right sibling of the left-child.
377400 position += 1 ;
378- } while (position > _length); // Happens if last element is a left child.
401+ } while (
402+ position > _queue.length); // Happens if last element is a left child.
379403 } while (position != 1 ); // At root again. Happens for right-most element.
380404 return - 1 ;
381405 }
382406
383- E _removeLast () {
384- var newLength = _length - 1 ;
385- var last = _elementAt (newLength);
386- _queue[newLength] = null ;
387- _length = newLength;
388- return last;
389- }
407+ E _removeLast () => _queue.removeLast ();
390408
391409 /// Place [element] in heap at [index] or above.
392410 ///
@@ -396,7 +414,7 @@ class HeapPriorityQueue<E> implements PriorityQueue<E> {
396414 void _bubbleUp (E element, int index) {
397415 while (index > 0 ) {
398416 var parentIndex = (index - 1 ) ~ / 2 ;
399- var parent = _elementAt ( parentIndex) ;
417+ var parent = _queue[ parentIndex] ;
400418 if (comparison (element, parent) > 0 ) break ;
401419 _queue[index] = parent;
402420 index = parentIndex;
@@ -411,10 +429,10 @@ class HeapPriorityQueue<E> implements PriorityQueue<E> {
411429 /// swap it with the highest priority child.
412430 void _bubbleDown (E element, int index) {
413431 var rightChildIndex = index * 2 + 2 ;
414- while (rightChildIndex < _length ) {
432+ while (rightChildIndex < _queue.length ) {
415433 var leftChildIndex = rightChildIndex - 1 ;
416- var leftChild = _elementAt ( leftChildIndex) ;
417- var rightChild = _elementAt ( rightChildIndex) ;
434+ var leftChild = _queue[ leftChildIndex] ;
435+ var rightChild = _queue[ rightChildIndex] ;
418436 var comp = comparison (leftChild, rightChild);
419437 int minChildIndex;
420438 E minChild;
@@ -435,8 +453,8 @@ class HeapPriorityQueue<E> implements PriorityQueue<E> {
435453 rightChildIndex = index * 2 + 2 ;
436454 }
437455 var leftChildIndex = rightChildIndex - 1 ;
438- if (leftChildIndex < _length ) {
439- var child = _elementAt ( leftChildIndex) ;
456+ if (leftChildIndex < _queue.length ) {
457+ var child = _queue[ leftChildIndex] ;
440458 var comp = comparison (element, child);
441459 if (comp > 0 ) {
442460 _queue[index] = child;
@@ -445,17 +463,6 @@ class HeapPriorityQueue<E> implements PriorityQueue<E> {
445463 }
446464 _queue[index] = element;
447465 }
448-
449- /// Grows the capacity of the list holding the heap.
450- ///
451- /// Called when the list is full.
452- void _grow () {
453- var newCapacity = _queue.length * 2 + 1 ;
454- if (newCapacity < _initialCapacity) newCapacity = _initialCapacity;
455- var newQueue = List <E ?>.filled (newCapacity, null );
456- newQueue.setRange (0 , _length, _queue);
457- _queue = newQueue;
458- }
459466}
460467
461468/// Implementation of [HeapPriorityQueue.unorderedElements] .
0 commit comments