Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1531)

Side by Side Diff: pkg/collection/lib/priority_queue.dart

Issue 110483006: Add priority queue to package:collection. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698