1 /*
2 pblHeap.c - C implementation of a binary heap.
3
4 Copyright (C) 2010 Peter Graf
5
6 This file is part of PBL - The Program Base Library.
7 PBL is free software.
8
9 This library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Lesser General Public
11 License as published by the Free Software Foundation; either
12 version 2.1 of the License, or (at your option) any later version.
13
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public
20 License along with this library; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
23 For more information on the Program Base Library or Peter Graf,
24 please see: http://www.mission-base.com/.
25
26 $Log: pblHeap.c,v $
27 Revision 1.5 2010/08/31 21:06:20 peter
28 Added the heap functions.
29
30
31 */
32
33 /*
34 * Make sure "strings <exe> | grep Id | sort -u" shows the source file versions
35 */
36 char* pblHeap_c_id = "$Id: pblHeap.c,v 1.5 2010/08/31 21:06:20 peter Exp $";
37
38 #include <stdio.h>
39 #include <memory.h>
40
41 #ifndef __APPLE__
42 #include <stdlib.h>
43 #endif
44
45 #include <stdlib.h>
46
47 #include "pbl.h"
48
49 /*****************************************************************************/
50 /* #defines */
51 /*****************************************************************************/
52
53 /*****************************************************************************/
54 /* Function declarations */
55 /*****************************************************************************/
56
57 /*****************************************************************************/
58 /* Functions */
59 /*****************************************************************************/
60
61 /**
62 * Creates a new heap.
63 *
64 * The pbl heap implementation is a
65 * <a href="http://en.wikipedia.org/wiki/Binary_heap">binary heap</a>
66 * using an
67 * <a href="http://www.mission-base.com/peter/source/pbl/doc/list.html">array list</a>
68 * as underlying data structure.
69 *
70 * This function has a time complexity of O(1).
71 *
72 * @return PblHeap * retPtr != NULL: A pointer to the new heap.
73 * @return PblHeap * retPtr == NULL: An error, see pbl_errno:
74 *
75 * <BR>PBL_ERROR_OUT_OF_MEMORY - Out of memory.
76 */
pblHeapNew(void)77 PblHeap * pblHeapNew( void )
78 {
79 return (PblHeap *)pblListNewArrayList();
80 }
81
82 /**
83 * Sets an application specific compare function for the elements
84 * of the heap.
85 *
86 * An application specific compare function can be set to the heap.
87 * If no specific compare function is specified by the user,
88 * the default compare function is used.
89 * The default compare function compares the two pointers directly,
90 * i.e. it tests for object identity.
91 *
92 * The compare function specified should behave like the one that
93 * can be specified for the C-library function 'qsort'.
94 *
95 * The arguments actually passed to the compare function when it is called
96 * are addresses of the element pointers added to the heap.
97 * E.g.: If you add char * pointers to the heap, the compare function
98 * will be called with char ** pointers as arguments. See the documentation
99 * for the C-library function 'qsort' for further information.
100 *
101 * This method has a time complexity of O(1).
102 *
103 * @return * retptr: The compare function used before, may be NULL.
104 */
pblHeapSetCompareFunction(PblHeap * heap,int (* compare)(const void * prev,const void * next))105 void * pblHeapSetCompareFunction( /* */
106 PblHeap * heap, /** The heap to set compare function for */
107 int(*compare) /** The compare function to set */
108 ( /* */
109 const void* prev, /** The "left" element for compare */
110 const void* next /** The "right" element for compare */
111 ) /* */
112 )
113 {
114 return pblListSetCompareFunction( (PblList *)heap, compare );
115 }
116
117 /**
118 * Removes all of the elements from the heap.
119 *
120 * <B>Note:</B> No memory of the elements themselves is freed.
121 *
122 * This function has a time complexity of O(N),
123 * with N being the number of elements in the heap.
124 *
125 * @return void
126 */
pblHeapClear(PblHeap * heap)127 void pblHeapClear( /* */
128 PblHeap * heap /** The heap to clear */
129 )
130 {
131 pblListClear( (PblList *)heap );
132 }
133
134 /**
135 * Frees the heap's memory from heap.
136 *
137 * <B>Note:</B> The memory of the elements themselves is not freed.
138 *
139 * This function has a time complexity of O(N),
140 * with N being the number of elements in the heap.
141 *
142 * @return void
143 */
pblHeapFree(PblHeap * heap)144 void pblHeapFree( /* */
145 PblHeap * heap /** The heap to free */
146 )
147 {
148 pblListClear( (PblList *)heap );
149 pblListFree( (PblList *)heap );
150 }
151
152 /**
153 * Increases the capacity of the heap, if necessary.
154 *
155 * This function ensures that the heap can hold
156 * at least the number of elements specified by the minimum
157 * capacity argument.
158 *
159 * If the capacity is actually increased,
160 * this function has a memory and time complexity of O(N),
161 * with N being the new capacity of the heap.
162 *
163 * @return int rc >= 0: OK, the heap capacity is returned.
164 * @return int rc < 0: An error, see pbl_errno:
165 *
166 * <BR>PBL_ERROR_OUT_OF_MEMORY - Out of memory.
167 */
pblHeapEnsureCapacity(PblHeap * heap,int minCapacity)168 int pblHeapEnsureCapacity( /* */
169 PblHeap * heap, /** The heap to use */
170 int minCapacity /** The desired minimum capacity */
171 )
172 {
173 return pblListEnsureCapacity( (PblList *)heap, minCapacity );
174 }
175
176 /**
177 * Returns the capacity of the heap.
178 *
179 * This function has a time complexity of O(1).
180 *
181 * @return int rc: The capacity of the heap.
182 */
pblHeapGetCapacity(PblHeap * heap)183 int pblHeapGetCapacity( /* */
184 PblHeap * heap /** The heap to use */
185 )
186 {
187 return pblListGetCapacity( (PblList *)heap );
188 }
189
190 /**
191 * Returns the number of elements in the heap.
192 *
193 * This function has a time complexity of O(1).
194 *
195 * @return int rc: The number of elements in the heap.
196 */
pblHeapSize(PblHeap * heap)197 int pblHeapSize( /* */
198 PblHeap * heap /** The heap to use */
199 )
200 {
201 return pblListSize( (PblList *)heap );
202 }
203
204 /**
205 * Tests if the heap has no elements.
206 *
207 * This function has a time complexity of O(1).
208 *
209 * @return int rc != 0: The heap has no elements.
210 * @return int rc == 0: The heap has elements.
211 */
pblHeapIsEmpty(PblHeap * heap)212 int pblHeapIsEmpty( /* */
213 PblHeap * heap /** The heap to use */
214 )
215 {
216 return pblListIsEmpty( (PblList *)heap );
217 }
218
219 /**
220 * Trims the capacity of the heap to the heap's current size.
221 *
222 * If the capacity is actually decreased,
223 * this function has a time complexity of O(N),
224 * with N being the number of elements in the heap.
225 *
226 * @return int rc >= 0: The capacity of the heap.
227 * @return int rc < 0: An error, see pbl_errno:
228 *
229 * <BR>PBL_ERROR_OUT_OF_MEMORY - Out of memory.
230 */
pblHeapTrimToSize(PblHeap * heap)231 int pblHeapTrimToSize( /* */
232 PblHeap * heap /** The heap to use */
233 )
234 {
235 return pblListTrimToSize( (PblList *)heap );
236 }
237
238 /*
239 * Ensures the heap condition of the heap downward in the "tree".
240 *
241 * This function has a time complexity of O(Log N).
242 *
243 * @return int rc: The new index of the element.
244 */
pblEnsureHeapConditionDownward(PblHeap * heap,int size,int index)245 static int pblEnsureHeapConditionDownward( PblHeap * heap, int size, int index )
246 {
247 // For zero based arrays all elements with an index bigger
248 // than 'size / 2 - 1' do not have any children
249 //
250 int lastParentIndex = size / 2 - 1;
251
252 if( index <= lastParentIndex )
253 {
254 void *entry = pblListGet( (PblList *)heap, index );
255
256 // As long as the entry has at least one child
257 //
258 while( index <= lastParentIndex )
259 {
260 // For zero based arrays the left child is at '2 * index + 1'
261 //
262 int childIndex = 2 * index + 1;
263
264 void *child = pblListGet( (PblList *)heap, childIndex );
265
266 // If the entry also has a right child, the bigger child
267 // needs to be considered for the swap
268 //
269 if( childIndex < size - 1 )
270 {
271 // The right child is next to the child in the array
272 //
273 int rightChildIndex = childIndex + 1;
274 void *rightChild =
275 pblListGet( (PblList *)heap, rightChildIndex );
276
277 if( pblCollectionElementCompare( (PblCollection*)heap,
278 rightChild, child ) > 0 )
279 {
280 // Use the right child for the swap
281 //
282 child = rightChild;
283 childIndex = rightChildIndex;
284 }
285 }
286
287 if( pblCollectionElementCompare( (PblCollection*)heap, entry, child )
288 >= 0 )
289 {
290 // The heap condition is fulfilled for the entry
291 //
292 return index;
293 }
294
295 // Do the swap with the child
296 //
297 pblListSet( (PblList *)heap, childIndex, entry );
298 pblListSet( (PblList *)heap, index, child );
299
300 index = childIndex;
301 }
302 }
303
304 // The entry does not have any children, therefore
305 // the heap condition is fulfilled for the entry
306 //
307 return index;
308 }
309
310 /*
311 * Ensures the heap condition of the heap upward in the "tree".
312 *
313 * This function has a time complexity of O(Log N).
314 *
315 * @return int rc: The new index of the element.
316 */
pblEnsureHeapConditionUpward(PblHeap * heap,int index)317 static int pblEnsureHeapConditionUpward( PblHeap * heap, int index )
318 {
319 if( index > 0 )
320 {
321 void *entry = pblListGet( (PblList *)heap, index );
322
323 // As long as the entry is not the top of the heap
324 //
325 while( index > 0 )
326 {
327 // For zero based arrays the parent index is '( index - 1 ) / 2'
328 //
329 int parentIndex = ( index - 1 ) / 2;
330 void *parent = pblListGet( (PblList *)heap, parentIndex );
331
332 if( pblCollectionElementCompare( (PblCollection*)heap, entry,
333 parent ) <= 0 )
334 {
335 // The heap condition is fulfilled for the entry
336 //
337 return index;
338 }
339
340 // Do the swap with the parent
341 //
342 pblListSet( (PblList *)heap, parentIndex, entry );
343 pblListSet( (PblList *)heap, index, parent );
344
345 index = parentIndex;
346 }
347 }
348
349 // The entry is the top of the heap
350 //
351 return index;
352 }
353
354 /**
355 * Ensures the heap condition of the heap for the element with the given index.
356 *
357 * This function has a time complexity of O(Log N).
358 *
359 * @return int rc: The new index of the element.
360 */
pblHeapEnsureCondition(PblHeap * heap,int index)361 int pblHeapEnsureCondition( /* */
362 PblHeap * heap, /** The heap to use */
363 int index /** Index of element to ensure condtion for */
364 )
365 {
366 int rc = pblEnsureHeapConditionUpward( heap, index );
367 if( rc == index )
368 {
369 rc = pblEnsureHeapConditionDownward( heap, pblHeapSize( heap ), index );
370 }
371 return rc;
372 }
373
374 /**
375 * Ensures the heap condition for the first element.
376 *
377 * This function has a time complexity of O(Log N).
378 *
379 * @return int rc: The new index of the element.
380 */
pblHeapEnsureConditionFirst(PblHeap * heap)381 int pblHeapEnsureConditionFirst( /**/
382 PblHeap * heap /** The heap to use */
383 )
384 {
385 return pblHeapEnsureCondition( heap, 0 );
386 }
387
388 /**
389 * Adds the element to the end of the heap without ensuring the
390 * heap condition.
391 *
392 * This function has a time complexity of O(1).
393 *
394 * This function together with \Ref{pblHeapConstruct}()
395 * can be used to build a heap with N elements
396 * in time proportional to N.
397 *
398 * First create an empty heap with \Ref{pblHeapNew}()
399 * and ensure the heap has space for N elements via a
400 * call to \Ref{pblHeapEnsureCapacity}(),
401 * then add all elements via calls to this function
402 * and finally ensure the heap condition with a call
403 * to \Ref{pblHeapConstruct}().
404 *
405 * @return int rc >= 0: The size of the heap.
406 * @return int rc < 0: An error, see pbl_errno:
407 *
408 * <BR>PBL_ERROR_OUT_OF_MEMORY - Out of memory.
409 */
pblHeapAddLast(PblHeap * heap,void * element)410 int pblHeapAddLast( /* */
411 PblHeap * heap, /** The heap to use */
412 void * element /** Element to be added to the heap */
413 )
414 {
415 return pblListAdd( (PblList *)heap, element );
416 }
417
418 /**
419 * Inserts the element into the heap and maintains the heap condition
420 * of the heap.
421 *
422 * This function has a time complexity of O(Log N),
423 * with N being the number of elements in the heap.
424 *
425 * @return int rc >= 0: The size of the heap.
426 * @return int rc < 0: An error, see pbl_errno:
427 *
428 * <BR>PBL_ERROR_OUT_OF_MEMORY - Out of memory.
429 */
pblHeapInsert(PblHeap * heap,void * element)430 int pblHeapInsert( /* */
431 PblHeap * heap, /** The heap to use */
432 void * element /** Element to be inserted to the heap */
433 )
434 {
435 // Add to the end of the heap
436 //
437 int rc = pblListAdd( (PblList *)heap, element );
438 if( rc > 1 )
439 {
440 // Ensure the heap condition for the last entry
441 //
442 pblEnsureHeapConditionUpward( heap, rc - 1 );
443 }
444 return rc;
445 }
446
447 /**
448 * Removes the last element from the heap,
449 * maintaining the heap condition of the heap.
450 *
451 * <B>Note:</B> The last element is not guaranteed to be
452 * the smallest element!
453 *
454 * This function has a time complexity of O(1).
455 *
456 * @return void* retptr != (void*)-1: The element removed.
457 * @return void* retptr == (void*)-1: An error see pbl_errno:
458 *
459 * <BR>PBL_ERROR_OUT_OF_BOUNDS - The heap is empty.
460 */
pblHeapRemoveLast(PblHeap * heap)461 void * pblHeapRemoveLast( /* */
462 PblHeap * heap /** The heap to use */
463 )
464 {
465 return pblListRemoveLast( (PblList *)heap );
466 }
467
468 /**
469 * Removes the element at the specified position from the
470 * heap, maintaining the heap condition of the heap.
471 *
472 * This function has a time complexity of O(Log N),
473 * with N being the number of elements in the heap.
474 *
475 * @return void* retptr != (void*)-1: The element removed.
476 * @return void* retptr == (void*)-1: An error see pbl_errno:
477 *
478 * <BR>PBL_ERROR_OUT_OF_BOUNDS - Index is out of range (index < 0 || index >= size()).
479 */
pblHeapRemoveAt(PblHeap * heap,int index)480 void * pblHeapRemoveAt( /* */
481 PblHeap * heap, /** The heap to use */
482 int index /** The index at which the element is to be removed */
483 )
484 {
485 void *retptr;
486 void *lastEntry;
487 void *entry;
488 int size = pblListSize( (PblList *)heap );
489
490 if( index == size - 1 )
491 {
492 return pblListRemoveLast( (PblList *)heap );
493 }
494
495 entry = pblListGet( (PblList *)heap, index );
496 if( entry == (void *)-1 )
497 {
498 return (void*)-1;
499 }
500
501 // Remove the last entry in the array list
502 //
503 lastEntry = pblListRemoveLast( (PblList *)heap );
504 if( lastEntry == (void *)-1 )
505 {
506 return (void*)-1;
507 }
508
509 // One entry was removed, therefore the size must be decreased as well
510 //
511 size -= 1;
512 if( size < 1 )
513 {
514 // The heap is empty now
515 //
516 return lastEntry;
517 }
518
519 retptr = pblListSet( (PblList *)heap, index, lastEntry );
520 if( retptr == (void *)-1 )
521 {
522 return (void*)-1;
523 }
524
525 if( size > 1 )
526 {
527 // Copying the values might have violated the heap condition
528 // at position index, it needs to ensured
529 //
530 pblEnsureHeapConditionDownward( heap, size, index );
531 }
532
533 return retptr;
534 }
535
536 /**
537 * Removes the biggest element from the heap,
538 * maintaining the heap condition of the heap.
539 *
540 * This function has a time complexity of O(Log N),
541 * with N being the number of elements in the heap.
542 *
543 * @return void* retptr != (void*)-1: The element removed.
544 * @return void* retptr == (void*)-1: An error see pbl_errno:
545 *
546 * <BR>PBL_ERROR_OUT_OF_BOUNDS - The heap is empty.
547 */
pblHeapRemoveFirst(PblHeap * heap)548 void * pblHeapRemoveFirst( /* */
549 PblHeap * heap /** The heap to use */
550 )
551 {
552 return pblHeapRemoveAt( heap, 0 );
553 }
554
555 /**
556 * Returns the element at the specified position in the heap.
557 *
558 * This method has a time complexity of O(1).
559 *
560 * @return void * retptr != (void*)-1: The element at the specified position, may be NULL.
561 * @return void * retptr == (void*)-1: An error, see pbl_errno:
562 *
563 * <BR>PBL_ERROR_OUT_OF_BOUNDS - Index is out of range (index < 0 || index >= size()).
564 */
pblHeapGet(PblHeap * heap,int index)565 void * pblHeapGet( /* */
566 PblHeap * heap, /** The heap to use */
567 int index /** Index of the element to return */
568 )
569 {
570 return pblListGet( (PblList *)heap, index );
571 }
572
573 /**
574 * Returns but does not remove the element at the top of the
575 * heap.
576 *
577 * This function has a time complexity of O(1).
578 *
579 * @return void* retptr != (void*)-1: The element returned.
580 * @return void* retptr == (void*)-1: An error see pbl_errno:
581 *
582 * <BR>PBL_ERROR_OUT_OF_BOUNDS - The heap is empty.
583 */
pblHeapGetFirst(PblHeap * heap)584 void * pblHeapGetFirst( /* */
585 PblHeap * heap /** The heap to use */
586 )
587 {
588 return pblListGet( (PblList *)heap, 0 );
589 }
590
591 /**
592 * Constructs a heap using 'bottom-up heap construction'.
593 *
594 * This function has a time complexity of O(N),
595 * with N being the number of elements in the heap.
596 *
597 * This function together with \Ref{pblHeapAddLast}()
598 * can be used to build a heap with N elements
599 * in time proportional to N.
600 *
601 * First create an empty heap with \Ref{pblHeapNew}()
602 * and ensure the heap has space for N elements via a
603 * call to \Ref{pblHeapEnsureCapacity}(),
604 * then add all elements via calls to \Ref{pblHeapAddLast}(),
605 * and finally ensure the heap condition with a call
606 * to this function.
607 */
pblHeapConstruct(PblHeap * heap)608 void pblHeapConstruct( /* */
609 PblHeap * heap /** The heap to use */
610 )
611 {
612 int size = pblListSize( (PblList *)heap );
613 int index;
614
615 // For zero based arrays all entries with an index bigger
616 // than 'size / 2 - 1' do not have any children.
617 //
618 // The heap condition is always fulfilled for entries without children,
619 // therefore 'bottom-up heap construction' only needs to look
620 // at elements that do have children
621 //
622 for( index = size / 2 - 1; index >= 0; index-- )
623 {
624 pblEnsureHeapConditionDownward( heap, size, index );
625 }
626 }
627
628 /**
629 * Returns an iterator over the elements in the heap.
630 *
631 * The iterator starts the iteration at the biggest element.
632 *
633 * <B>Note</B>: The memory allocated by this method for the iterator returned needs to be released
634 * by calling \Ref{pblIteratorFree}() once the iterator is no longer needed.
635 *
636 * Modifying the heap via the Iterator's own remove or add methods
637 * does not maintain the heap property of the heap.
638 * In this case the heap property has to be restored by a call
639 * to \Ref{pblHeapConstruct}().
640 *
641 * The iterators returned by the this method are fail-fast:
642 * if the heap is structurally modified at any time after the iterator is created,
643 * in any way except through the Iterator's own remove or add methods,
644 * the iterator will return a PBL_ERROR_CONCURRENT_MODIFICATION error.
645 *
646 * Thus, in the face of concurrent modification,
647 * the iterator fails quickly and cleanly,
648 * rather than risking arbitrary, non-deterministic
649 * behavior at an undetermined time in the future.
650 *
651 * This method has a time complexity of O(1).
652 *
653 * @return void * retptr != NULL: The iterator.
654 * @return void * retptr == NULL: An error, see pbl_errno:
655 *
656 * <BR>PBL_ERROR_OUT_OF_MEMORY - Out of memory.
657 */
pblHeapIterator(PblHeap * heap)658 PblIterator * pblHeapIterator( /* */
659 PblHeap * heap /** The heap to use */
660 )
661 {
662 return pblIteratorNew( (PblList *)heap );
663 }
664
665 /**
666 * Joins the two heaps by moving all elements of the 'other'
667 * heap. When this function returns, 'other' will be empty.
668 *
669 * This function has a time complexity of O(N), with N
670 * being the number of elements in the heap after the join.
671 *
672 * @return int rc >= 0: The size of the heap after tjhe join.
673 * @return int rc < 0: An error, see pbl_errno:
674 *
675 * <BR>PBL_ERROR_OUT_OF_MEMORY - Out of memory.
676 */
pblHeapJoin(PblHeap * heap,PblHeap * other)677 int pblHeapJoin( /* */
678 PblHeap * heap, /** The heap to join to */
679 PblHeap * other /** The other heap to join */
680 )
681 {
682 int otherSize = pblListSize( (PblList *)other );
683 int size = pblListSize( (PblList *)heap );
684
685 if( otherSize == 0 )
686 {
687 // Joining from an empty 'other', nothing to do
688 //
689 return size;
690 }
691
692 // Make sure there is enough space for all the
693 // elements having to be moved
694 //
695 size += otherSize;
696 if( pblListEnsureCapacity( (PblList *)heap, size ) < 0 )
697 {
698 return -1;
699 }
700
701 // Low level entry copy from 'other' to 'heap'
702 //
703 if( pblListAddAll( (PblList *)heap, other ) < 0 )
704 {
705 return -1;
706 }
707
708 // The entries have been copied to 'heap',
709 // they can be cleared from 'other', thus implementing a move
710 //
711 pblListClear( (PblList *)other );
712
713 // If 'size == otherSize', then 'heap' was originally empty,
714 // as 'other' fulfilled the heap condition before the join
715 // and the low level entry copy did not violate it,
716 // the heap condition now holds for the result of the join
717 //
718 if( size == otherSize )
719 {
720 return size;
721 }
722
723 // The entries of 'other' where appended to the entries
724 // of 'heap', potentially breaking the heap condition,
725 // therefore the heap condition needs to ensured anew
726 //
727 pblHeapConstruct( heap );
728
729 return size;
730 }
731