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