diff options
Diffstat (limited to '')
| -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 | ||
