Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

root/trunk/tango/util/container/more/Heap.d

Revision 5425, 11.8 kB (checked in by larsivi, 2 years ago)

version->debug (UnitTest?), Unittest -> UnitTest? ++

  • Property svn:eol-style set to native
Line 
1 /**
2   *
3   * Copyright:  Copyright (C) 2008 Chris Wright.  All rights reserved.
4   * License:    BSD style: $(LICENSE)
5   * Version:    Oct 2008: Initial release
6   * Author:     Chris Wright, aka dhasenan
7   *
8   */
9
10 module tango.util.container.more.Heap;
11
12 private import tango.core.Exception;
13
14 bool minHeapCompare(T)(T a, T b) {return a <= b;}
15 bool maxHeapCompare(T)(T a, T b) {return a >= b;}
16 void defaultHeapSwap(T)(T t, uint index) {}
17
18 /** A heap is a data structure where you can insert items in random order and extract them in sorted order.
19   * Pushing an element into the heap takes O(lg n) and popping the top of the heap takes O(lg n). Heaps are
20   * thus popular for sorting, among other things.
21   *
22   * No opApply is provided, since most people would expect this to return the contents in sorted order,
23   * not do significant heap allocation, not modify the collection, and complete in linear time. This
24   * combination is not possible with a heap.
25   *
26   * Note: always pass by reference when modifying a heap.
27   *
28   * The template arguments to the heap are:
29   *     T       = the element type
30   *     Compare = a function called when ordering elements. Its signature should be bool(T, T).
31   *               see minHeapCompare() and maxHeapCompare() for examples.
32   *     Move    = a function called when swapping elements. Its signature should be void(T, uint).
33   *               The default does nothing, and should suffice for most users. You
34   *               probably want to keep this function small; it's called O(log N)
35   *               times per insertion or removal.
36 */
37
38 struct Heap (T, alias Compare = minHeapCompare!(T), alias Move = defaultHeapSwap!(T))
39 {
40         alias pop       remove;
41         alias push      opCatAssign;
42
43         // The actual data.
44         private T[]     heap;
45        
46         // The index of the cell into which the next element will go.
47         private uint    next;
48
49         /** Inserts the given element into the heap. */
50         void push (T t)
51         {
52                 auto index = next++;
53                 while (heap.length <= index)
54                        heap.length = 2 * heap.length + 32;
55
56                 heap [index] = t;
57                 Move (t, index);
58                 fixup (index);
59         }
60
61         /** Inserts all elements in the given array into the heap. */
62         void push (T[] array)
63         {
64                 if (heap.length < next + array.length)
65                         heap.length = next + array.length + 32;
66
67                 foreach (t; array) push (t);
68         }
69
70         /** Removes the top of this heap and returns it. */
71         T pop ()
72         {
73                 return removeAt (0);
74         }
75
76         /** Remove the every instance that matches the given item. */
77         void removeAll (T t)
78         {
79                 // TODO: this is slower than it could be.
80                 // I am reasonably certain we can do the O(n) scan, but I want to
81                 // look at it a bit more.
82                 while (remove (t)) {}
83         }
84
85         /** Remove the first instance that matches the given item.
86           * Returns: true iff the item was found, otherwise false. */
87         bool remove (T t)
88         {
89                 foreach (i, a; heap)
90                 {
91                         if (a is t || a == t)
92                         {
93                                 removeAt (i);
94                                 return true;
95                         }
96                 }
97                 return false;
98         }
99
100         /** Remove the element at the given index from the heap.
101           * The index is according to the heap's internal layout; you are
102           * responsible for making sure the index is correct.
103           * The heap invariant is maintained. */
104         T removeAt (uint index)
105         {
106                 if (next <= index)
107                 {
108                         throw new NoSuchElementException ("Heap :: tried to remove an"
109                                 ~ " element with index greater than the size of the heap "
110                                 ~ "(did you call pop() from an empty heap?)");
111                 }
112                 next--;
113                 auto t = heap[index];
114                 // if next == index, then we have nothing valid on the heap
115                 // so popping does nothing but change the length
116                 // the other calls are irrelevant, but we surely don't want to
117                 // call Move with invalid data
118                 if (next > index)
119                 {
120                         heap[index] = heap[next];
121                         Move(heap[index], index);
122                         fixdown(index);
123
124                         // added via ticket 1885 (kudos to wolfwood)
125                         if (heap[index] is heap[next])
126                             fixup(index);
127                 }
128                 return t;
129         }
130
131         /** Gets the value at the top of the heap without removing it. */
132         T peek ()
133         {
134                 assert (next > 0);
135                 return heap[0];
136         }
137
138         /** Returns the number of elements in this heap. */
139         uint size ()
140         {
141                 return next;
142         }
143
144         /** Reset this heap. */
145         void clear ()
146         {
147                 next = 0;
148         }
149
150         /** reset this heap, and use the provided host for value elements */
151         void clear (T[] host)
152         {
153                 this.heap = host;
154                 clear;
155         }
156
157         /** Get the reserved capacity of this heap. */
158         uint capacity ()
159         {
160                 return heap.length;
161         }
162
163         /** 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. */
164         uint capacity (uint value)
165         {
166                 if (value < next)
167                 {
168                         throw new IllegalArgumentException ("Heap :: illegal truncation");
169                 }
170                 heap.length = value;
171                 return value;
172         }
173
174         /** Return a shallow copy of this heap. */
175         Heap clone ()
176         {
177                 Heap other;
178                 other.heap = this.heap.dup;
179                 other.next = this.next;
180                 return other;
181         }
182
183         // Get the index of the parent for the element at the given index.
184         private uint parent (uint index)
185         {
186                 return (index - 1) / 2;
187         }
188
189         // Having just inserted, restore the heap invariant (that a node's value is greater than its children)
190         private void fixup (uint index)
191         {
192                 if (index == 0) return;
193                 uint par = parent (index);
194                 if (!Compare(heap[par], heap[index]))
195                 {
196                         swap (par, index);
197                         fixup (par);
198                 }
199         }
200
201         // Having just removed and replaced the top of the heap with the last inserted element,
202         // restore the heap invariant.
203         private void fixdown (uint index)
204         {
205                 uint left = 2 * index + 1;
206                 uint down;
207                 if (left >= next)
208                 {
209                         return;
210                 }
211
212                 if (left == next - 1)
213                 {
214                         down = left;
215                 }
216                 else if (Compare (heap[left], heap[left + 1]))
217                 {
218                         down = left;
219                 }
220                 else
221                 {
222                         down = left + 1;
223                 }
224
225                 if (!Compare(heap[index], heap[down]))
226                 {
227                         swap (index, down);
228                         fixdown (down);
229                 }
230         }
231
232         // Swap two elements in the array.
233         private void swap (uint a, uint b)
234         {
235                 auto t1 = heap[a];
236                 auto t2 = heap[b];
237                 heap[a] = t2;
238                 Move(t2, a);
239                 heap[b] = t1;
240                 Move(t1, b);
241         }
242 }
243
244
245 /** A minheap implementation. This will have the smallest item as the top of the heap.
246   *
247   * Note: always pass by reference when modifying a heap.
248   *
249 */
250
251 template MinHeap(T)
252 {
253         alias Heap!(T, minHeapCompare) MinHeap;
254 }
255
256 /** A maxheap implementation. This will have the largest item as the top of the heap.
257   *
258   * Note: always pass by reference when modifying a heap.
259   *
260 */
261
262 template MaxHeap(T)
263 {
264         alias Heap!(T, maxHeapCompare) MaxHeap;
265 }
266
267
268
269 debug (UnitTest)
270 {
271 unittest
272 {
273         MinHeap!(uint) h;
274         assert (h.size is 0);
275         h ~= 1;
276         h ~= 3;
277         h ~= 2;
278         h ~= 4;
279         assert (h.size is 4);
280
281         assert (h.peek is 1);
282         assert (h.peek is 1);
283         assert (h.size is 4);
284         h.pop;
285         assert (h.peek is 2);
286         assert (h.size is 3);
287 }
288
289 unittest
290 {
291         MinHeap!(uint) h;
292         assert (h.size is 0);
293         h ~= 1;
294         h ~= 3;
295         h ~= 2;
296         h ~= 4;
297         assert (h.size is 4);
298
299         assert (h.pop is 1);
300         assert (h.size is 3);
301         assert (h.pop is 2);
302         assert (h.size is 2);
303         assert (h.pop is 3);
304         assert (h.size is 1);
305         assert (h.pop is 4);
306         assert (h.size is 0);
307 }
308
309 unittest
310 {
311         MaxHeap!(uint) h;
312         h ~= 1;
313         h ~= 3;
314         h ~= 2;
315         h ~= 4;
316
317         assert (h.pop is 4);
318         assert (h.pop is 3);
319         assert (h.pop is 2);
320         assert (h.pop is 1);
321 }
322
323 unittest
324 {
325         MaxHeap!(uint) h;
326         h ~= 1;
327         h ~= 3;
328         h ~= 2;
329         h ~= 4;
330         h.remove(3);
331         assert (h.pop is 4);
332         assert (h.pop is 2);
333         assert (h.pop is 1);
334         assert (h.size == 0);
335 }
336
337 long[] swapped;
338 uint[] indices;
339 void onMove(long a, uint b)
340 {
341         swapped ~= a;
342         indices ~= b;
343 }
344 unittest
345 {
346         // this tests that onMove is called with fixdown
347         swapped = null;
348         indices = null;
349         Heap!(long, minHeapCompare, onMove) h;
350         // no swap
351         h ~= 1;
352         // no swap
353         h ~= 3;
354
355         // onMove() is called for all insertions
356         swapped = null;
357         indices = null;
358         // pop: you replace the top with the last and
359         // percolate down. So you have to swap once when
360         // popping at a minimum, and that's if you have only two
361         // items in the heap.
362         assert (h.pop is 1);
363         assert (swapped.length == 1, "" ~ cast(char)('a' + swapped.length));
364         assert (swapped[0] == 3);
365         assert (indices[0] == 0);
366         assert (h.pop is 3);
367         assert (swapped.length == 1, "" ~ cast(char)('a' + swapped.length));
368 }
369 unittest
370 {
371         // this tests that onMove is called with fixup
372         swapped = null;
373         indices = null;
374         Heap!(long, minHeapCompare, onMove) h;
375         // no swap
376         h ~= 1;
377         // no swap
378         h ~= 3;
379         // swap: move 0 to position 0, 1 to position 2
380         h ~= 0;
381         int n=3; // onMove() called for insertions too
382         if (swapped[n] == 0)
383         {
384                 assert (swapped[n+1] == 1);
385                 assert (indices[n+0] == 0);
386                 assert (indices[n+1] == 2);
387         }
388         else
389         {
390                 assert (swapped[n+1] == 0);
391                 assert (swapped[n+0] == 1);
392                 assert (indices[n+0] == 2);
393                 assert (indices[n+1] == 0);
394         }
395 }
396
397 unittest
398 {
399         MaxHeap!(uint) h;
400         h ~= 1;
401         h ~= 3;
402         h ~= 2;
403         h ~= 4;
404         auto other = h.clone;
405
406         assert (other.pop is 4);
407         assert (other.pop is 3);
408         assert (other.pop is 2);
409         assert (other.pop is 1);
410         assert (h.size is 4, "cloned heap shares data with original heap");
411         assert (h.pop is 4, "cloned heap shares data with original heap");
412         assert (h.pop is 3, "cloned heap shares data with original heap");
413         assert (h.pop is 2, "cloned heap shares data with original heap");
414         assert (h.pop is 1, "cloned heap shares data with original heap");
415 }
416 }
Note: See TracBrowser for help on using the browser.