summaryrefslogtreecommitdiff
path: root/src/heap.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/heap.h')
-rw-r--r--src/heap.h300
1 files changed, 203 insertions, 97 deletions
diff --git a/src/heap.h b/src/heap.h
index 9df4121..31c2435 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -13,76 +13,93 @@
13#include "bu/exceptionbase.h" 13#include "bu/exceptionbase.h"
14#include "bu/util.h" 14#include "bu/util.h"
15#include "bu/queue.h" 15#include "bu/queue.h"
16#include "bu/sharedcore.h"
16 17
17namespace Bu 18namespace Bu
18{ 19{
19 subExceptionDecl( HeapException ); 20 subExceptionDecl( HeapException );
20 21
21 /** 22 template<typename item, typename cmpfunc, typename itemalloc>
22 * A priority queue that allows for an unlimited number of priorities. All 23 class Heap;
23 * objects enqueued must support less-than-comparison. Then every time an 24
24 * item is dequeued it is always the least item in the heap. The heap 25 /** @cond DEVEL */
25 * operates using a binary tree for storage, which allows most operations 26 template<typename item, typename cmpfunc, typename itemalloc>
26 * to be very fast. Enqueueing and dequeueing are both O(log(N)) operatoins 27 class HeapCore
27 * whereas peeking is constant time.
28 *
29 * This heap implementation allows iterating, however please note that any
30 * enqueue or dequeue operation will invalidate the iterator and make it
31 * unusable (if it still works, you shouldn't trust the results). Also,
32 * the items are not stored in memory in order, they are optomized into a
33 * tree. This means that the items will be in effectively random order
34 * while iterating through them, and the order cannot be trusted. Also,
35 * modifying an item in the heap will not cause that item to be re-sorted.
36 * If you want to change the position of an item in the heap you will have
37 * to dequeue every item before it, dequeue that item, change it, and
38 * re-enqueue all of the items removed.
39 */
40 template<typename item, typename cmpfunc=__basicLTCmp<item>, typename itemalloc=std::allocator<item> >
41 class Heap : public Queue<item>
42 { 28 {
43 public: 29 friend class Heap<item, cmpfunc, itemalloc>;
44 Heap() : 30 friend class SharedCore<
45 iSize( 7 ), 31 Heap<item, cmpfunc, itemalloc>, HeapCore<item, cmpfunc, itemalloc>
32 >;
33 private:
34 HeapCore() :
35 iSize( 0 ),
46 iFill( 0 ), 36 iFill( 0 ),
47 aItem( ia.allocate( iSize ) ) 37 aItem( NULL )
48 { 38 {
49 } 39 }
50 40
51 Heap( cmpfunc cmpin ) : 41 virtual ~HeapCore()
52 iSize( 7 ),
53 iFill( 0 ),
54 aItem( ia.allocate( iSize ) ),
55 cmp( cmpin )
56 { 42 {
43 clear();
57 } 44 }
58 45
59 Heap( int iInitialCapacity ) : 46 void init()
60 iSize( 0 ),
61 iFill( 0 ),
62 aItem( NULL )
63 { 47 {
64 for( iSize = 1; iSize < iInitialCapacity; iSize=iSize*2+1 ) { } 48 if( iSize > 0 )
49 return;
50
51 iSize = 7;
52 iFill = 0;
65 aItem = ia.allocate( iSize ); 53 aItem = ia.allocate( iSize );
66 } 54 }
67 55
68 Heap( cmpfunc cmpin, int iInitialCapacity ) : 56 void init( int iCap )
69 iSize( 0 ),
70 iFill( 0 ),
71 aItem( NULL ),
72 cmp( cmpin )
73 { 57 {
74 for( iSize = 1; iSize < iInitialCapacity; iSize=iSize*2+1 ) { } 58 if( iSize > 0 )
59 return;
60
61 for( iSize = 1; iSize < iCap; iSize=iSize*2+1 ) { }
62 iFill = 0;
75 aItem = ia.allocate( iSize ); 63 aItem = ia.allocate( iSize );
76 } 64 }
77 65
78 virtual ~Heap() 66 void clear()
79 { 67 {
68 if( iSize == 0 )
69 return;
70
80 for( int j = 0; j < iFill; j++ ) 71 for( int j = 0; j < iFill; j++ )
81 ia.destroy( &aItem[j] ); 72 ia.destroy( &aItem[j] );
82 ia.deallocate( aItem, iSize ); 73 ia.deallocate( aItem, iSize );
83 aItem = NULL; 74 aItem = NULL;
75 iSize = 0;
76 iFill = 0;
84 } 77 }
78
79 void upSize()
80 {
81 if( iSize == 0 )
82 {
83 init();
84 return;
85 }
85 86
87 item *aNewItems = ia.allocate( iSize*2+1 );
88 //
89 // We cannot use a memcopy here because we don't know what kind
90 // of datastructures are being used, we have to copy them one at
91 // a time.
92 //
93 for( int j = 0; j < iFill; j++ )
94 {
95 ia.construct( &aNewItems[j], aItem[j] );
96 ia.destroy( &aItem[j] );
97 }
98 ia.deallocate( aItem, iSize );
99 aItem = aNewItems;
100 iSize = iSize*2+1;
101 }
102
86 virtual void enqueue( const item &it ) 103 virtual void enqueue( const item &it )
87 { 104 {
88 item i = it; // TODO: This is a silly workaround, put the i item 105 item i = it; // TODO: This is a silly workaround, put the i item
@@ -126,20 +143,6 @@ namespace Bu
126 iFill++; 143 iFill++;
127 } 144 }
128 145
129 virtual item &peek()
130 {
131 if( iFill == 0 )
132 throw HeapException("Heap empty.");
133 return aItem[0];
134 }
135
136 virtual const item &peek() const
137 {
138 if( iFill == 0 )
139 throw HeapException("Heap empty.");
140 return aItem[0];
141 }
142
143 virtual item dequeue() 146 virtual item dequeue()
144 { 147 {
145 if( iFill == 0 ) 148 if( iFill == 0 )
@@ -174,14 +177,118 @@ namespace Bu
174 return iRet; 177 return iRet;
175 } 178 }
176 179
180 private:
181 int iSize;
182 int iFill;
183 item *aItem;
184 cmpfunc cmp;
185 itemalloc ia;
186 };
187 /** @endcond */
188
189 /**
190 * A priority queue that allows for an unlimited number of priorities. All
191 * objects enqueued must support less-than-comparison. Then every time an
192 * item is dequeued it is always the least item in the heap. The heap
193 * operates using a binary tree for storage, which allows most operations
194 * to be very fast. Enqueueing and dequeueing are both O(log(N)) operatoins
195 * whereas peeking is constant time.
196 *
197 * This heap implementation allows iterating, however please note that any
198 * enqueue or dequeue operation will invalidate the iterator and make it
199 * unusable (if it still works, you shouldn't trust the results). Also,
200 * the items are not stored in memory in order, they are optomized into a
201 * tree. This means that the items will be in effectively random order
202 * while iterating through them, and the order cannot be trusted. Also,
203 * modifying an item in the heap will not cause that item to be re-sorted.
204 * If you want to change the position of an item in the heap you will have
205 * to dequeue every item before it, dequeue that item, change it, and
206 * re-enqueue all of the items removed.
207 */
208 template<typename item, typename cmpfunc=__basicLTCmp<item>, typename itemalloc=std::allocator<item> >
209 class Heap : public Queue<item>, public SharedCore<
210 Heap<item, cmpfunc, itemalloc>,
211 HeapCore<item, cmpfunc, itemalloc>
212 >
213 {
214 private:
215 typedef class Heap<item,cmpfunc,itemalloc> MyType;
216 typedef class HeapCore<item,cmpfunc,itemalloc> Core;
217
218 protected:
219 using SharedCore<MyType, Core>::core;
220 using SharedCore<MyType, Core>::_hardCopy;
221 using SharedCore<MyType, Core>::_resetCore;
222 using SharedCore<MyType, Core>::_allocateCore;
223
224 public:
225 Heap()
226 {
227 }
228
229 Heap( cmpfunc cmpin )
230 {
231 core->cmp = cmpin;
232 }
233
234 Heap( int iInitialCapacity )
235 {
236 core->init( iInitialCapacity );
237 }
238
239 Heap( cmpfunc cmpin, int iInitialCapacity )
240 {
241 core->cmp = cmpin;
242 core->init( iInitialCapacity );
243 }
244
245 Heap( const MyType &rSrc ) :
246 SharedCore<MyType, Core>( rSrc )
247 {
248 }
249
250 virtual ~Heap()
251 {
252 }
253
254 virtual void enqueue( const item &it )
255 {
256 _hardCopy();
257
258 core->enqueue( it );
259 }
260
261 virtual item &peek()
262 {
263 _hardCopy();
264
265 if( core->iFill == 0 )
266 throw HeapException("Heap empty.");
267 return core->aItem[0];
268 }
269
270 virtual const item &peek() const
271 {
272 if( core->iFill == 0 )
273 throw HeapException("Heap empty.");
274 return core->aItem[0];
275 }
276
277 virtual item dequeue()
278 {
279 _hardCopy();
280
281 return core->dequeue();
282 }
283
177 virtual bool isEmpty() const 284 virtual bool isEmpty() const
178 { 285 {
179 return (iFill==0); 286 return (core->iFill==0);
180 } 287 }
181 288
182 virtual int getSize() const 289 virtual int getSize() const
183 { 290 {
184 return iFill; 291 return core->iFill;
185 } 292 }
186 293
187 class iterator 294 class iterator
@@ -201,7 +308,7 @@ namespace Bu
201 { 308 {
202 if( pHeap == NULL ) 309 if( pHeap == NULL )
203 throw Bu::ExceptionBase("Iterator not initialized."); 310 throw Bu::ExceptionBase("Iterator not initialized.");
204 if( iIndex < 0 || iIndex >= pHeap->iFill ) 311 if( iIndex < 0 || iIndex >= pHeap->core->iFill )
205 throw Bu::ExceptionBase("Iterator out of bounds."); 312 throw Bu::ExceptionBase("Iterator out of bounds.");
206 } 313 }
207 314
@@ -230,12 +337,16 @@ namespace Bu
230 337
231 item &operator*() 338 item &operator*()
232 { 339 {
233 return pHeap->aItem[iIndex]; 340 pHeap->_hardCopy();
341
342 return pHeap->core->aItem[iIndex];
234 } 343 }
235 344
236 item *operator->() 345 item *operator->()
237 { 346 {
238 return &(pHeap->aItem[iIndex]); 347 pHeap->_hardCopy();
348
349 return &(pHeap->core->aItem[iIndex]);
239 } 350 }
240 351
241 iterator &operator++() 352 iterator &operator++()
@@ -260,7 +371,7 @@ namespace Bu
260 { 371 {
261 checkValid(); 372 checkValid();
262 iIndex++; 373 iIndex++;
263 if( iIndex >= pHeap->iFill ) 374 if( iIndex >= pHeap->core->iFill )
264 iIndex = -1; 375 iIndex = -1;
265 376
266 return *this; 377 return *this;
@@ -279,7 +390,7 @@ namespace Bu
279 checkValid(); 390 checkValid();
280 iterator ret( *this ); 391 iterator ret( *this );
281 ret.iIndex += iDelta; 392 ret.iIndex += iDelta;
282 if( ret.iIndex >= pHeap->iFill ) 393 if( ret.iIndex >= pHeap->core->iFill )
283 ret.iIndex = -1; 394 ret.iIndex = -1;
284 return ret; 395 return ret;
285 } 396 }
@@ -294,12 +405,12 @@ namespace Bu
294 return ret; 405 return ret;
295 } 406 }
296 407
297 operator bool() 408 operator bool() const
298 { 409 {
299 return iIndex != -1; 410 return iIndex != -1;
300 } 411 }
301 412
302 bool isValid() 413 bool isValid() const
303 { 414 {
304 return iIndex != -1; 415 return iIndex != -1;
305 } 416 }
@@ -328,7 +439,7 @@ namespace Bu
328 { 439 {
329 if( pHeap == NULL ) 440 if( pHeap == NULL )
330 throw Bu::ExceptionBase("Iterator not initialized."); 441 throw Bu::ExceptionBase("Iterator not initialized.");
331 if( iIndex < 0 || iIndex >= pHeap->iFill ) 442 if( iIndex < 0 || iIndex >= pHeap->core->iFill )
332 throw Bu::ExceptionBase("Iterator out of bounds."); 443 throw Bu::ExceptionBase("Iterator out of bounds.");
333 } 444 }
334 445
@@ -363,19 +474,23 @@ namespace Bu
363 474
364 const item &operator*() 475 const item &operator*()
365 { 476 {
366 return pHeap->aItem[iIndex]; 477 pHeap->_hardCopy();
478
479 return pHeap->core->aItem[iIndex];
367 } 480 }
368 481
369 const item *operator->() 482 const item *operator->()
370 { 483 {
371 return &(pHeap->aItem[iIndex]); 484 pHeap->_hardCopy();
485
486 return &(pHeap->core->aItem[iIndex]);
372 } 487 }
373 488
374 const_iterator &operator++() 489 const_iterator &operator++()
375 { 490 {
376 checkValid(); 491 checkValid();
377 iIndex++; 492 iIndex++;
378 if( iIndex >= pHeap->iFill ) 493 if( iIndex >= pHeap->core->iFill )
379 iIndex = -1; 494 iIndex = -1;
380 495
381 return *this; 496 return *this;
@@ -393,7 +508,7 @@ namespace Bu
393 { 508 {
394 checkValid(); 509 checkValid();
395 iIndex++; 510 iIndex++;
396 if( iIndex >= pHeap->iFill ) 511 if( iIndex >= pHeap->core->iFill )
397 iIndex = -1; 512 iIndex = -1;
398 513
399 return *this; 514 return *this;
@@ -427,12 +542,12 @@ namespace Bu
427 return ret; 542 return ret;
428 } 543 }
429 544
430 operator bool() 545 operator bool() const
431 { 546 {
432 return iIndex != -1; 547 return iIndex != -1;
433 } 548 }
434 549
435 bool isValid() 550 bool isValid() const
436 { 551 {
437 return iIndex != -1; 552 return iIndex != -1;
438 } 553 }
@@ -452,14 +567,14 @@ namespace Bu
452 567
453 iterator begin() 568 iterator begin()
454 { 569 {
455 if( iFill == 0 ) 570 if( core->iFill == 0 )
456 return end(); 571 return end();
457 return iterator( this, 0 ); 572 return iterator( this, 0 );
458 } 573 }
459 574
460 const_iterator begin() const 575 const_iterator begin() const
461 { 576 {
462 if( iFill == 0 ) 577 if( core->iFill == 0 )
463 return end(); 578 return end();
464 return const_iterator( this, 0 ); 579 return const_iterator( this, 0 );
465 } 580 }
@@ -475,31 +590,22 @@ namespace Bu
475 } 590 }
476 591
477 592
478 private: 593 protected:
479 void upSize() 594 virtual Core *_copyCore( Core *src )
480 { 595 {
481 item *aNewItems = ia.allocate( iSize*2+1 ); 596 Core *pRet = _allocateCore();
482 // 597
483 // We cannot use a memcopy here because we don't know what kind 598 pRet->iSize = src->iSize;
484 // of datastructures are being used, we have to copy them one at 599 pRet->iFill = src->iFill;
485 // a time. 600 pRet->cmp = src->cmp;
486 // 601 pRet->aItem = pRet->ia.allocate( pRet->iSize );
487 for( int j = 0; j < iFill; j++ ) 602 for( int j = 0; j < pRet->iFill; j++ )
488 { 603 {
489 ia.construct( &aNewItems[j], aItem[j] ); 604 pRet->ia.construct( &pRet->aItem[j], src->aItem[j] );
490 ia.destroy( &aItem[j] );
491 } 605 }
492 ia.deallocate( aItem, iSize );
493 aItem = aNewItems;
494 iSize = iSize*2+1;
495 }
496 606
497 private: 607 return pRet;
498 int iSize; 608 }
499 int iFill;
500 item *aItem;
501 cmpfunc cmp;
502 itemalloc ia;
503 }; 609 };
504}; 610};
505 611