Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
|
Anders Johnsen
2014/01/05 20:37:12
2014 :)
Lasse Reichstein Nielsen
2014/01/06 12:53:01
I'm still in the "academic quarter" - three months
| |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library dart.pkg.collection.priority_queue; | |
| 6 | |
| 7 import "dart:collection" show SplayTreeSet; | |
| 8 | |
| 9 /** | |
| 10 * A priority queue is a priority based work-list of elements. | |
| 11 * | |
| 12 * It is optimized for adding elements | |
|
Søren Gjesse
2014/01/06 08:05:21
Should this be the performance characteristics for
Lasse Reichstein Nielsen
2014/01/06 12:53:01
Let's postpone having a default implementation unt
| |
| 13 * and removing them again in order of highest priority. | |
| 14 */ | |
| 15 abstract class PriorityQueue<E> { | |
| 16 /** | |
| 17 * Number of elements in the queue. | |
| 18 */ | |
| 19 int get length; | |
| 20 | |
| 21 /** | |
| 22 * Whether the queue is empty. | |
| 23 */ | |
| 24 bool get isEmpty; | |
| 25 | |
| 26 /** | |
| 27 * Whether the queue has any elements. | |
| 28 */ | |
| 29 bool get isNotEmpty; | |
| 30 | |
| 31 /** | |
| 32 * Checks if [object] is in the queue. | |
| 33 * | |
| 34 * Returns true if the element is found. | |
| 35 * | |
| 36 * This operation is not efficient. It may need to look through all elements | |
|
Anders Johnsen
2014/01/05 20:37:12
Don't talk about efficiency in the interface. That
Lasse Reichstein Nielsen
2014/01/06 12:53:01
Done.
| |
| 37 * in order see if an element is there. | |
| 38 */ | |
| 39 bool contains(E object); | |
| 40 | |
| 41 /** | |
| 42 * Returns the next element that will be returned by [removeFirst]. | |
| 43 * | |
| 44 * The element is not removed from the queue. | |
| 45 * | |
| 46 * The queue must not be empty when this method is called. | |
|
Søren Gjesse
2014/01/06 08:05:21
Say what happen in this case?
Lasse Reichstein Nielsen
2014/01/06 12:53:01
This is how we write it when the implementation th
| |
| 47 */ | |
| 48 E get first; | |
| 49 | |
| 50 /** | |
| 51 * Removes and returns the element with the highest priority. | |
| 52 * | |
| 53 * Repeatedly calling this method, without adding element in between, | |
| 54 * is guaranteed to return elements in non-decreasing order as, specified by | |
| 55 * [comparison]. | |
| 56 * | |
| 57 * The queue must not be empty when this method is called. | |
| 58 */ | |
| 59 E removeFirst(); | |
| 60 | |
| 61 /** | |
| 62 * Removes an element that compares equal to [element] in the queue. | |
| 63 * | |
| 64 * Returns true if an element is found and removed, | |
| 65 * and false if not equal element is found. | |
| 66 * | |
| 67 * This operation is not efficient. It may need to look through all elements | |
| 68 * in order see if an element is there. | |
| 69 */ | |
| 70 bool remove(E element); | |
| 71 | |
| 72 /** | |
| 73 * Removes all the elements from this queue and returns them. | |
| 74 * | |
| 75 * The returned iterable has no specified order. | |
|
Søren Gjesse
2014/01/06 08:05:21
Why is the priority order not used in the returned
Lasse Reichstein Nielsen
2014/01/06 12:53:01
Because you can use toList to do that.
This is an
| |
| 76 */ | |
| 77 Iterable<E> removeAll(); | |
| 78 | |
| 79 /** | |
| 80 * Removes all the elements from this queue. | |
| 81 */ | |
| 82 void clear(); | |
| 83 | |
| 84 /** | |
| 85 * Returns a list of the elements of this queue in priority order. | |
|
Søren Gjesse
2014/01/06 08:05:21
I assume the elements are not removed by this oper
Lasse Reichstein Nielsen
2014/01/06 12:53:01
Done.
| |
| 86 * | |
| 87 * The order is the order that the elements would be in if they were | |
| 88 * removed from this queue using [removeFirst]. | |
| 89 */ | |
| 90 List<E> toList(); | |
| 91 | |
| 92 /** | |
| 93 * Return a comparator based set using the comparator of this queue. | |
| 94 * | |
| 95 * The returned [Set] is a [SplayTreeSet], but this may change. | |
|
Anders Johnsen
2014/01/05 20:37:12
How will that work with a priority-queue of [4, 4,
Lasse Reichstein Nielsen
2014/01/06 12:53:01
The same way [4,4,4,4].toSet() works - it returns
| |
| 96 * The set contains all the elements of this queue. | |
| 97 */ | |
| 98 Set<E> toSet(); | |
| 99 } | |
| 100 | |
| 101 /** | |
| 102 * Heap based priority queue. | |
| 103 * | |
| 104 * The elements are kept in a heap structure, where the least element | |
|
Anders Johnsen
2014/01/05 20:37:12
least -> `first` ?
Lasse Reichstein Nielsen
2014/01/06 12:53:01
-> the element with the highest priority
| |
| 105 * is immediately accessible, and adding or removing an element takes | |
| 106 * at logarithmic time on average (but occasionally linear when growing | |
|
Anders Johnsen
2014/01/05 20:37:12
amortized?
Lasse Reichstein Nielsen
2014/01/06 12:53:01
Done.
| |
| 107 * the heap capacity). | |
| 108 */ | |
| 109 class HeapPriorityQueue<E> implements PriorityQueue<E> { | |
| 110 // Keep capacity as a power of 2 minus one. Any odd number will work, | |
| 111 // but bubbling down assumes an odd length so either both children of a | |
| 112 // node is inside the queue, or both are not. | |
| 113 static const int _INITIAL_CAPACITY = 7; | |
| 114 | |
| 115 /** The comparison being used to compare the priority of elements. */ | |
|
Søren Gjesse
2014/01/06 08:05:21
Multiline or ///.
Lasse Reichstein Nielsen
2014/01/06 12:53:01
But why?!?
Ok, multiline.
| |
| 116 final Comparator comparison; | |
| 117 | |
| 118 /** | |
| 119 * List implementation of a heap. | |
| 120 */ | |
| 121 List<E> _queue = new List<E>(_INITIAL_CAPACITY); | |
| 122 | |
| 123 /** | |
| 124 * Number of elements in queue. | |
| 125 * | |
| 126 * The heap is implemented in the first [_length] entries of [_queue]. | |
| 127 */ | |
| 128 int _length = 0; | |
| 129 | |
| 130 /** | |
| 131 * Create a new priority queue. | |
| 132 * | |
| 133 * The [comparison] is a [Comparator] used to compare the priority of | |
| 134 * elements. An element that compares as less than another element has | |
| 135 * a higher priority. | |
|
Søren Gjesse
2014/01/06 08:05:21
Add some information about the default value for c
Lasse Reichstein Nielsen
2014/01/06 12:53:01
Done.
| |
| 136 */ | |
| 137 HeapPriorityQueue([int comparison(E e1, E e2)]) | |
| 138 : comparison = (comparison != null) ? comparison : Comparable.compare; | |
| 139 | |
| 140 int get length => _length; | |
| 141 | |
| 142 bool get isEmpty => _length == 0; | |
| 143 | |
| 144 bool get isNotEmpty => _length != 0; | |
| 145 | |
| 146 void _add(E element) { | |
|
Anders Johnsen
2014/01/05 20:37:12
Move privates below non-private.
Lasse Reichstein Nielsen
2014/01/06 12:53:01
Hmm. Ok, grudgingly.
I sorted the public methods
| |
| 147 if (_length == _queue.length) _grow(); | |
| 148 _bubbleUp(element, _length++); | |
| 149 } | |
| 150 | |
| 151 /** Find the index of an object in the heap. Returns -1 if not found. */ | |
|
Anders Johnsen
2014/01/05 20:37:12
Multi-line or //.
Søren Gjesse
2014/01/06 08:05:21
This should not be a dartdoc comment.
Lasse Reichstein Nielsen
2014/01/06 12:53:01
Multi-line done.
Why should it not be a dart-doc
| |
| 152 int _locate(E object) { | |
| 153 if (_length == 0) return -1; | |
| 154 // Count positions from one instad of zero. This gives the numbers | |
| 155 // some nice properties. For example, all right children are odd, | |
| 156 // their left sibling is even, and the parent is found by shifting | |
| 157 // right by one. | |
| 158 // Valid range for position is [1.._length], inclusive. | |
| 159 int position = 1; | |
| 160 // Pre-order depth first search, omit child nodes if the current | |
| 161 // node has lower priority than [object], because all nodes lower | |
| 162 // in the heap will also have lower priority. | |
| 163 do { | |
| 164 int index = position - 1; | |
| 165 E element = _queue[index]; | |
| 166 int comp = comparison(element, object); | |
| 167 if (comp == 0) return index; | |
| 168 if (comp < 0) { | |
| 169 // Element may be in subtree. | |
| 170 // Continue with the left child, if it is there. | |
| 171 int leftChildPosition = position * 2; | |
| 172 if (leftChildPosition <= _length) { | |
| 173 position = leftChildPosition; | |
| 174 continue; | |
| 175 } | |
| 176 } | |
| 177 // Find the next right sibling or right ancestor sibling. | |
| 178 do { | |
| 179 while (position.isOdd) { | |
| 180 position >>= 1; | |
|
Anders Johnsen
2014/01/05 20:37:12
// Go to parent.
Lasse Reichstein Nielsen
2014/01/06 12:53:01
Done.
| |
| 181 } | |
| 182 position += 1; | |
|
Anders Johnsen
2014/01/05 20:37:12
// Go to right sibling.
Lasse Reichstein Nielsen
2014/01/06 12:53:01
Done.
| |
| 183 } while (position > _length); | |
| 184 } while (position != 1); // This happens for the right-most element. | |
| 185 return -1; | |
| 186 } | |
| 187 | |
| 188 bool contains(E object) { | |
| 189 return _locate(object) >= 0; | |
| 190 } | |
| 191 | |
| 192 bool remove(E element) { | |
| 193 int index = _locate(element); | |
| 194 if (index < 0) return false; | |
| 195 E last = _removeLast(); | |
| 196 if (index < _length) { | |
| 197 int comp = comparison(last, element); | |
|
Anders Johnsen
2014/01/05 20:37:12
no need to store as comp?
Lasse Reichstein Nielsen
2014/01/06 12:53:01
Done.
| |
| 198 if (comp <= 0) { | |
| 199 _bubbleUp(last, index); | |
| 200 } else { | |
| 201 _bubbleDown(last, index); | |
| 202 } | |
| 203 } | |
|
Søren Gjesse
2014/01/06 08:05:21
Should there be a shrink policy as well to shrink
Lasse Reichstein Nielsen
2014/01/06 12:53:01
Probably not.
The complexity is unlikely to be wor
| |
| 204 return true; | |
| 205 } | |
| 206 | |
| 207 E _removeLast() { | |
| 208 int newLength = _length - 1; | |
|
Anders Johnsen
2014/01/05 20:37:12
Start out by saying `--_length;` and use _length i
Lasse Reichstein Nielsen
2014/01/06 12:53:01
I dislike having an element in _queue[_length], so
| |
| 209 E last = _queue[newLength]; | |
| 210 _queue[newLength] = null; | |
| 211 _length = newLength; | |
| 212 return last; | |
| 213 } | |
| 214 | |
| 215 void add(E element) { | |
|
Anders Johnsen
2014/01/05 20:37:12
=>
Lasse Reichstein Nielsen
2014/01/06 12:53:01
Not for a void function.
| |
| 216 _add(element); | |
| 217 } | |
| 218 | |
| 219 void addAll(Iterable<E> elements) { | |
| 220 for (E element in elements) { | |
| 221 _add(element); | |
| 222 } | |
| 223 } | |
| 224 | |
| 225 E get first { | |
| 226 if (_length == 0) throw new StateError("No such element"); | |
|
Anders Johnsen
2014/01/05 20:37:12
if (isEmpty), and below in removeFirst.
Lasse Reichstein Nielsen
2014/01/06 12:53:01
No.
I try to avoid defining methods in terms of ot
| |
| 227 return _queue[0]; | |
|
Anders Johnsen
2014/01/05 20:37:12
.first
Lasse Reichstein Nielsen
2014/01/06 12:53:01
No in general, and ... this is the definition of `
| |
| 228 } | |
| 229 | |
| 230 E removeFirst() { | |
| 231 if (_length == 0) throw new StateError("No such element"); | |
| 232 E result = _queue[0]; | |
|
Anders Johnsen
2014/01/05 20:37:12
.first
Lasse Reichstein Nielsen
2014/01/06 12:53:01
And no.
| |
| 233 E last = _removeLast(); | |
| 234 if (_length > 0) { | |
| 235 _bubbleDown(last, 0); | |
| 236 } | |
| 237 return result; | |
| 238 } | |
| 239 | |
| 240 Iterable<E> removeAll() { | |
| 241 List<E> result = _queue; | |
| 242 int length = _length; | |
| 243 _queue = const []; | |
| 244 _length = 0; | |
| 245 return result.take(length); | |
| 246 } | |
| 247 | |
| 248 void clear() { | |
| 249 _queue = const []; | |
| 250 _length = 0; | |
| 251 } | |
| 252 | |
| 253 List<E> toList() { | |
| 254 List<E> list = new List<E>()..length = _length; | |
| 255 list.setRange(0, _length, _queue); | |
| 256 list.sort(comparison); | |
| 257 return list; | |
| 258 } | |
| 259 | |
| 260 Set<E> toSet() { | |
| 261 Set<E> set = new SplayTreeSet<E>(comparison); | |
| 262 for (int i = 0; i < _length; i++) { | |
| 263 set.add(_queue[i]); | |
| 264 } | |
| 265 return set; | |
| 266 } | |
| 267 | |
| 268 /** | |
| 269 * Place [element] in heap at [index] or above. | |
| 270 * | |
| 271 * Put element into the empty cell at `index`. | |
| 272 * While the `element` has higher priority than the | |
| 273 * parent, swap it with the parent. | |
| 274 */ | |
| 275 void _bubbleUp(E element, int index) { | |
| 276 while (index > 0) { | |
| 277 int parentIndex = (index - 1) ~/ 2; | |
| 278 E parent = _queue[parentIndex]; | |
| 279 int comp = comparison(element, parent); | |
| 280 if (comp > 0) break; | |
| 281 _queue[index] = parent; | |
| 282 index = parentIndex; | |
| 283 } | |
| 284 _queue[index] = element; | |
| 285 } | |
| 286 | |
| 287 /** | |
| 288 * Place [element] in heap at [index] or above. | |
| 289 * | |
| 290 * Put element into the empty cell at `index`. | |
| 291 * While the `element` has lower priority than either child, | |
| 292 * swap it with the highest priority child. | |
| 293 */ | |
| 294 void _bubbleDown(E element, int index) { | |
| 295 int rightChildIndex = index * 2 + 2; | |
| 296 while (rightChildIndex < _length) { | |
| 297 int leftChildIndex = rightChildIndex - 1; | |
| 298 E leftChild = _queue[leftChildIndex]; | |
| 299 E rightChild = _queue[rightChildIndex]; | |
| 300 int comp = comparison(leftChild, rightChild); | |
| 301 int minChildIndex; | |
| 302 E minChild; | |
| 303 if (comp < 0) { | |
| 304 minChild = leftChild; | |
| 305 minChildIndex = leftChildIndex; | |
| 306 } else { | |
| 307 minChild = rightChild; | |
| 308 minChildIndex = rightChildIndex; | |
| 309 } | |
| 310 comp = comparison(element, minChild); | |
| 311 if (comp <= 0) { | |
| 312 _queue[index] = element; | |
| 313 return; | |
| 314 } | |
| 315 _queue[index] = minChild; | |
| 316 index = minChildIndex; | |
| 317 rightChildIndex = index * 2 + 2; | |
| 318 } | |
| 319 int leftChildIndex = rightChildIndex - 1; | |
| 320 if (leftChildIndex < _length) { | |
| 321 E child = _queue[leftChildIndex]; | |
| 322 int comp = comparison(element, child); | |
| 323 if (comp > 0) { | |
| 324 _queue[index] = child; | |
| 325 index = leftChildIndex; | |
| 326 } | |
| 327 } | |
| 328 _queue[index] = element; | |
| 329 } | |
| 330 | |
| 331 void _grow() { | |
| 332 int newCapacity = _queue.length * 2 + 1; | |
| 333 if (newCapacity < _INITIAL_CAPACITY) newCapacity = _INITIAL_CAPACITY; | |
|
Anders Johnsen
2014/01/05 20:37:12
Emm... Why? :)
Lasse Reichstein Nielsen
2014/01/06 12:53:01
Because `clear` and `removeAll` sets the _queue to
| |
| 334 List<E> newQueue = new List<E>(newCapacity); | |
| 335 newQueue.setRange(0, _length, _queue); | |
| 336 _queue = newQueue; | |
| 337 } | |
| 338 | |
| 339 String toString() { | |
| 340 return _queue.take(_length).toString(); | |
|
Anders Johnsen
2014/01/05 20:37:12
.toList().sort(comparison)?
Lasse Reichstein Nielsen
2014/01/06 12:53:01
That would be wasteful.
I could live with:
"He
| |
| 341 } | |
| 342 } | |
| OLD | NEW |