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