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