diff options
Diffstat (limited to 'src/heap.h')
-rw-r--r-- | src/heap.h | 300 |
1 files changed, 203 insertions, 97 deletions
@@ -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 | ||
17 | namespace Bu | 18 | namespace 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 | ||