1 /**
2 *
3 * Copyright:
4 * Copyright (C) 2008 Chris Wright.
5 * Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH.
6 * All rights reserved.
7 *
8 * License:
9 * Tango Dual License: 3-Clause BSD License / Academic Free License v3.0.
10 * See LICENSE_TANGO.txt for details.
11 *
12 * Version: Oct 2008: Initial release
13 *
14 * Authors: Chris Wright, aka dhasenan
15 *
16 */17 18 moduleocean.util.container.more.Heap;
19 20 importocean.core.ExceptionDefinitions;
21 22 version (unittest) importocean.core.Test;
23 24 boolminHeapCompare(T)(Ta, Tb) {returna <= b;}
25 boolmaxHeapCompare(T)(Ta, Tb) {returna >= b;}
26 voiddefaultHeapSwap(T)(Tt, size_tindex) {}
27 28 /** A heap is a data structure where you can insert items in random order and extract them in sorted order.
29 * Pushing an element into the heap takes O(lg n) and popping the top of the heap takes O(lg n). Heaps are
30 * thus popular for sorting, among other things.
31 *
32 * No opApply is provided, since most people would expect this to return the contents in sorted order,
33 * not do significant heap allocation, not modify the collection, and complete in linear time. This
34 * combination is not possible with a heap.
35 *
36 * Note: always pass by reference when modifying a heap.
37 *
38 * The template arguments to the heap are:
39 * T = the element type
40 * Compare = a function called when ordering elements. Its signature should be bool(T, T).
41 * see minHeapCompare() and maxHeapCompare() for examples.
42 * Move = a function called when swapping elements. Its signature should be void(T, uint).
43 * The default does nothing, and should suffice for most users. You
44 * probably want to keep this function small; it's called O(log N)
45 * times per insertion or removal.
46 */47 48 structHeap (T, aliasCompare = minHeapCompare!(T), aliasMove = defaultHeapSwap!(T))
49 {
50 importocean.core.Verify;
51 52 aliaspopremove;
53 54 publictemplateopOpAssign ( stringop : "~" )
55 {
56 aliasopOpAssign = push;
57 }
58 59 // The actual data.60 privateT[] heap;
61 62 // The index of the cell into which the next element will go.63 privateuintnext;
64 65 /** Inserts the given element into the heap. */66 voidpush (Tt)
67 {
68 autoindex = next++;
69 while (heap.length <= index)
70 heap.length = 2 * heap.length + 32;
71 72 heap [index] = t;
73 Move (t, index);
74 fixup (index);
75 }
76 77 /** Inserts all elements in the given array into the heap. */78 voidpush (T[] array)
79 {
80 if (heap.length < next + array.length)
81 heap.length = next + array.length + 32;
82 83 foreach (t; array) push (t);
84 }
85 86 /** Removes the top of this heap and returns it. */87 Tpop ()
88 {
89 returnremoveAt (0);
90 }
91 92 /** Remove the every instance that matches the given item. */93 voidremoveAll (Tt)
94 {
95 // TODO: this is slower than it could be.96 // I am reasonably certain we can do the O(n) scan, but I want to97 // look at it a bit more.98 while (remove (t)) {}
99 }
100 101 /** Remove the first instance that matches the given item.
102 * Returns: true iff the item was found, otherwise false. */103 boolremove (Tt)
104 {
105 foreach (i, a; heap)
106 {
107 if (aist || a == t)
108 {
109 removeAt (i);
110 returntrue;
111 }
112 }
113 returnfalse;
114 }
115 116 /** Remove the element at the given index from the heap.
117 * The index is according to the heap's internal layout; you are
118 * responsible for making sure the index is correct.
119 * The heap invariant is maintained. */120 TremoveAt (size_tindex)
121 {
122 if (next <= index)
123 {
124 thrownewNoSuchElementException ("Heap :: tried to remove an"125 ~ " element with index greater than the size of the heap "126 ~ "(did you call pop() from an empty heap?)");
127 }
128 next--;
129 autot = heap[index];
130 // if next == index, then we have nothing valid on the heap131 // so popping does nothing but change the length132 // the other calls are irrelevant, but we surely don't want to133 // call Move with invalid data134 if (next > index)
135 {
136 heap[index] = heap[next];
137 Move(heap[index], index);
138 fixdown(index);
139 140 // added via ticket 1885 (kudos to wolfwood)141 if (heap[index] isheap[next])
142 fixup(index);
143 }
144 returnt;
145 }
146 147 /** Gets the value at the top of the heap without removing it. */148 Tpeek ()
149 {
150 verify (next > 0);
151 returnheap[0];
152 }
153 154 /** Returns the number of elements in this heap. */155 size_tsize ()
156 {
157 returnnext;
158 }
159 160 /** Reset this heap. */161 voidclear ()
162 {
163 next = 0;
164 }
165 166 /** reset this heap, and use the provided host for value elements */167 voidclear (T[] host)
168 {
169 this.heap = host;
170 clear;
171 }
172 173 /** Get the reserved capacity of this heap. */174 size_tcapacity ()
175 {
176 returnheap.length;
177 }
178 179 /** Reserve enough space in this heap for value elements. The reserved space is truncated or extended as necessary. If the value is less than the number of elements already in the heap, throw an exception. */180 uintcapacity (uintvalue)
181 {
182 if (value < next)
183 {
184 thrownewIllegalArgumentException ("Heap :: illegal truncation");
185 }
186 heap.length = value;
187 returnvalue;
188 }
189 190 /** Return a shallow copy of this heap. */191 Heapclone ()
192 {
193 Heapother;
194 other.heap = this.heap.dup;
195 other.next = this.next;
196 returnother;
197 }
198 199 // Get the index of the parent for the element at the given index.200 privatesize_tparent (size_tindex)
201 {
202 verify (index > 0);
203 return (index - 1) / 2;
204 }
205 206 // Having just inserted, restore the heap invariant (that a node's value is greater than its children)207 privatevoidfixup (size_tindex)
208 {
209 if (index == 0) return;
210 autopar = parent (index);
211 if (!Compare(heap[par], heap[index]))
212 {
213 swap (par, index);
214 fixup (par);
215 }
216 }
217 218 // Having just removed and replaced the top of the heap with the last inserted element,219 // restore the heap invariant.220 privatevoidfixdown (size_tindex)
221 {
222 size_tleft = 2 * index + 1;
223 size_tdown;
224 if (left >= next)
225 {
226 return;
227 }
228 229 if (left == next - 1)
230 {
231 down = left;
232 }
233 elseif (Compare (heap[left], heap[left + 1]))
234 {
235 down = left;
236 }
237 else238 {
239 down = left + 1;
240 }
241 242 if (!Compare(heap[index], heap[down]))
243 {
244 swap (index, down);
245 fixdown (down);
246 }
247 }
248 249 // Swap two elements in the array.250 privatevoidswap (size_ta, size_tb)
251 {
252 autot1 = heap[a];
253 autot2 = heap[b];
254 heap[a] = t2;
255 Move(t2, a);
256 heap[b] = t1;
257 Move(t1, b);
258 }
259 }
260 261 262 /** A minheap implementation. This will have the smallest item as the top of the heap.
263 *
264 * Note: always pass by reference when modifying a heap.
265 *
266 */267 268 templateMinHeap(T)
269 {
270 aliasHeap!(T, minHeapCompare) MinHeap;
271 }
272 273 /** A maxheap implementation. This will have the largest item as the top of the heap.
274 *
275 * Note: always pass by reference when modifying a heap.
276 *
277 */278 279 templateMaxHeap(T)
280 {
281 aliasHeap!(T, maxHeapCompare) MaxHeap;
282 }
283 284 285 286 unittest287 {
288 MinHeap!(size_t) h;
289 test (h.sizeis0);
290 h ~= 1;
291 h ~= 3;
292 h ~= 2;
293 h ~= 4;
294 test (h.sizeis4);
295 296 test (h.peekis1);
297 test (h.peekis1);
298 test (h.sizeis4);
299 h.pop;
300 test (h.peekis2);
301 test (h.sizeis3);
302 }
303 304 unittest305 {
306 MinHeap!(size_t) h;
307 test (h.sizeis0);
308 h ~= 1;
309 h ~= 3;
310 h ~= 2;
311 h ~= 4;
312 test (h.sizeis4);
313 314 test (h.popis1);
315 test (h.sizeis3);
316 test (h.popis2);
317 test (h.sizeis2);
318 test (h.popis3);
319 test (h.sizeis1);
320 test (h.popis4);
321 test (h.sizeis0);
322 }
323 324 unittest325 {
326 MaxHeap!(size_t) h;
327 h ~= 1;
328 h ~= 3;
329 h ~= 2;
330 h ~= 4;
331 332 test (h.popis4);
333 test (h.popis3);
334 test (h.popis2);
335 test (h.popis1);
336 }
337 338 unittest339 {
340 MaxHeap!(size_t) h;
341 h ~= 1;
342 h ~= 3;
343 h ~= 2;
344 h ~= 4;
345 h.remove(3);
346 test (h.popis4);
347 test (h.popis2);
348 test (h.popis1);
349 test (h.size == 0);
350 }
351 352 version (unittest)
353 {
354 long[] swapped;
355 size_t[] indices;
356 voidonMove(longa, size_tb)
357 {
358 swapped ~= a;
359 indices ~= b;
360 }
361 }
362 363 unittest364 {
365 // this tests that onMove is called with fixdown366 swapped = null;
367 indices = null;
368 Heap!(long, minHeapCompare, onMove) h;
369 // no swap370 h ~= 1;
371 // no swap372 h ~= 3;
373 374 // onMove() is called for all insertions375 swapped = null;
376 indices = null;
377 // pop: you replace the top with the last and378 // percolate down. So you have to swap once when379 // popping at a minimum, and that's if you have only two380 // items in the heap.381 test (h.popis1);
382 test (swapped.length == 1, "" ~ cast(char)('a' + swapped.length));
383 test (swapped[0] == 3);
384 test (indices[0] == 0);
385 test (h.popis3);
386 test (swapped.length == 1, "" ~ cast(char)('a' + swapped.length));
387 }
388 unittest389 {
390 // this tests that onMove is called with fixup391 swapped = null;
392 indices = null;
393 Heap!(long, minHeapCompare, onMove) h;
394 // no swap395 h ~= 1;
396 // no swap397 h ~= 3;
398 // swap: move 0 to position 0, 1 to position 2399 h ~= 0;
400 intn=3; // onMove() called for insertions too401 if (swapped[n] == 0)
402 {
403 test (swapped[n+1] == 1);
404 test (indices[n+0] == 0);
405 test (indices[n+1] == 2);
406 }
407 else408 {
409 test (swapped[n+1] == 0);
410 test (swapped[n+0] == 1);
411 test (indices[n+0] == 2);
412 test (indices[n+1] == 0);
413 }
414 }
415 416 unittest417 {
418 MaxHeap!(uint) h;
419 h ~= 1;
420 h ~= 3;
421 h ~= 2;
422 h ~= 4;
423 autoother = h.clone;
424 425 test (other.popis4);
426 test (other.popis3);
427 test (other.popis2);
428 test (other.popis1);
429 test (h.sizeis4, "cloned heap shares data with original heap");
430 test (h.popis4, "cloned heap shares data with original heap");
431 test (h.popis3, "cloned heap shares data with original heap");
432 test (h.popis2, "cloned heap shares data with original heap");
433 test (h.popis1, "cloned heap shares data with original heap");
434 }