1 /*
2  pblPriorityQueue.c - C implementation of a binary max-heap based priority queue.
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: pblPriorityQueue.c,v $
27  Revision 1.12  2010/10/01 20:44:31  peter
28  Port to 64 bit windows
29 
30  Revision 1.11  2010/08/29 15:29:31  peter
31  Added the heap functions.
32 
33  Revision 1.10  2010/08/20 20:10:25  peter
34  Implemented the priority queue functions.
35 
36 
37  */
38 
39 /*
40  * Make sure "strings <exe> | grep Id | sort -u" shows the source file versions
41  */
42 char* pblPriorityQueue_c_id =
43         "$Id: pblPriorityQueue.c,v 1.12 2010/10/01 20:44:31 peter Exp $";
44 
45 #include <stdio.h>
46 #include <memory.h>
47 
48 #ifndef __APPLE__
49 #include <stdlib.h>
50 #endif
51 
52 #include <stdlib.h>
53 
54 #include "pbl.h"
55 
56 /*****************************************************************************/
57 /* #defines                                                                  */
58 /*****************************************************************************/
59 
60 /*****************************************************************************/
61 /* Function declarations                                                     */
62 /*****************************************************************************/
63 
64 /*****************************************************************************/
65 /* Functions                                                                 */
66 /*****************************************************************************/
67 
68 /*
69  * Compares two priority queue entries.
70  *
71  * Used as compare function for priority queues.
72  *
73  * @return int rc  < 0: left is smaller than right
74  * @return int rc == 0: left and right are equal
75  * @return int rc  > 0: left is greater than right
76  */
PblPriorityQueueEntryCompareFunction(const void * left,const void * right)77 static int PblPriorityQueueEntryCompareFunction( /*      */
78 const void * left, /* The left value for the comparison  */
79 const void * right /* The right value for the comparison */
80 )
81 {
82     PblPriorityQueueEntry * leftEntry = *(PblPriorityQueueEntry**)left;
83     PblPriorityQueueEntry * rightEntry = *(PblPriorityQueueEntry**)right;
84 
85     if( !leftEntry )
86     {
87         if( rightEntry )
88         {
89             return -1;
90         }
91         return 0;
92     }
93     if( !rightEntry )
94     {
95         return 1;
96     }
97 
98     if( leftEntry->priority < rightEntry->priority )
99     {
100         return -1;
101     }
102     if( leftEntry->priority == rightEntry->priority )
103     {
104         return 0;
105     }
106     return 1;
107 }
108 
109 /**
110  * Creates a new priority queue.
111  *
112  * The priority queue implementation is a
113  * <a href="http://en.wikipedia.org/wiki/Binary_heap">binary max-heap</a> using
114  * an array list as underlying data structure.
115  * The entries kept in the array list are of type \Ref{PblPriorityQueueEntry}.
116  * Each entry holding the 'void *' element payload and the 'int' priority
117  * associated with the element.
118  *
119  * This function has a time complexity of O(1).
120  *
121  * @return PblPriorityQueue * retPtr != NULL: A pointer to the new priority queue.
122  * @return PblPriorityQueue * retPtr == NULL: An error, see pbl_errno:
123  *
124  * <BR>PBL_ERROR_OUT_OF_MEMORY - Out of memory.
125  */
pblPriorityQueueNew(void)126 PblPriorityQueue * pblPriorityQueueNew( void )
127 {
128     PblHeap * pblHeap = pblHeapNew();
129     if( !pblHeap )
130     {
131         return NULL;
132     }
133 
134     pblHeapSetCompareFunction( pblHeap, PblPriorityQueueEntryCompareFunction );
135 
136     return (PblPriorityQueue *)pblHeap;
137 }
138 
139 /**
140  * Removes all of the elements from the priority queue.
141  *
142  * <B>Note:</B> No memory of the elements themselves is freed.
143  *
144  * This function has a time complexity of O(N),
145  * with N being the number of elements in the priority queue.
146  *
147  * @return void
148  */
pblPriorityQueueClear(PblPriorityQueue * queue)149 void pblPriorityQueueClear( /*                  */
150 PblPriorityQueue * queue /** The queue to clear */
151 )
152 {
153     int index;
154 
155     // Loop over the heap and free the entries allocated
156     // by pblPriorityQueueAddLast()
157     //
158     for( index = pblHeapSize( (PblHeap *)queue ) - 1; index >= 0; index-- )
159     {
160         void * ptr = pblHeapGet( (PblHeap *)queue, index );
161         if( ptr != (void*)-1 )
162         {
163             PBL_FREE(ptr);
164         }
165     }
166     pblHeapClear( (PblHeap *)queue );
167 }
168 
169 /**
170  * Frees the priority queue's memory from heap.
171  *
172  * <B>Note:</B> The memory of the elements themselves is not freed.
173  *
174  * This function has a time complexity of O(N),
175  * with N being the number of elements in the priority queue.
176  *
177  * @return void
178  */
pblPriorityQueueFree(PblPriorityQueue * queue)179 void pblPriorityQueueFree( /*                  */
180 PblPriorityQueue * queue /** The queue to free */
181 )
182 {
183     pblPriorityQueueClear( queue );
184     pblHeapFree( (PblHeap *)queue );
185 }
186 
187 /**
188  * Increases the capacity of the priority queue, if necessary.
189  *
190  * This function ensures that the priority queue can hold
191  * at least the number of elements specified by the minimum
192  * capacity argument.
193  *
194  * If the capacity is actually increased,
195  * this function has a memory and time complexity of O(N),
196  * with N being the new capacity of the queue.
197  *
198  * @return int rc >= 0: OK, the queue capacity is returned.
199  * @return int rc <  0: An error, see pbl_errno:
200  *
201  * <BR>PBL_ERROR_OUT_OF_MEMORY - Out of memory.
202  */
pblPriorityQueueEnsureCapacity(PblPriorityQueue * queue,int minCapacity)203 int pblPriorityQueueEnsureCapacity( /*           */
204 PblPriorityQueue * queue, /** The queue to use   */
205 int minCapacity /** The desired minimum capacity */
206 )
207 {
208     return pblHeapEnsureCapacity( (PblHeap *)queue, minCapacity );
209 }
210 
211 /**
212  * Returns the capacity of the priority queue.
213  *
214  * This function has a time complexity of O(1).
215  *
216  * @return int rc: The capacity of the queue.
217  */
pblPriorityQueueGetCapacity(PblPriorityQueue * queue)218 int pblPriorityQueueGetCapacity( /*           */
219 PblPriorityQueue * queue /** The queue to use */
220 )
221 {
222     return pblHeapGetCapacity( (PblHeap *)queue );
223 }
224 
225 /**
226  * Returns the number of elements in the priority queue.
227  *
228  * This function has a time complexity of O(1).
229  *
230  * @return int rc: The number of elements in the priority queue.
231  */
pblPriorityQueueSize(PblPriorityQueue * queue)232 int pblPriorityQueueSize( /**/
233 PblPriorityQueue * queue /** The queue to use */
234 )
235 {
236     return pblHeapSize( (PblHeap *)queue );
237 }
238 
239 /**
240  * Tests if the priority queue has no elements.
241  *
242  * This function has a time complexity of O(1).
243  *
244  * @return int rc != 0: The queue has no elements.
245  * @return int rc == 0: The queue has elements.
246  */
pblPriorityQueueIsEmpty(PblPriorityQueue * queue)247 int pblPriorityQueueIsEmpty( /*               */
248 PblPriorityQueue * queue /** The queue to use */
249 )
250 {
251     return pblHeapIsEmpty( (PblHeap *)queue );
252 }
253 
254 /**
255  * Trims the capacity of the queue to the priority queue's current size.
256  *
257  * If the capacity is actually decreased,
258  * this function has a time complexity of O(N),
259  * with N being the number of elements in the priority queue.
260  *
261  * @return int rc >= 0: The capacity of the queue.
262  * @return int rc <  0: An error, see pbl_errno:
263  *
264  * <BR>PBL_ERROR_OUT_OF_MEMORY - Out of memory.
265  */
pblPriorityQueueTrimToSize(PblPriorityQueue * queue)266 int pblPriorityQueueTrimToSize( /*            */
267 PblPriorityQueue * queue /** The queue to use */
268 )
269 {
270     return pblHeapTrimToSize( (PblHeap *)queue );
271 }
272 
273 /**
274  * Adds the element with the specified priority to the
275  * end of the priority queue without ensuring the
276  * heap condition.
277  *
278  * This function has a time complexity of O(1).
279  *
280  * This function together with \Ref{pblPriorityQueueConstruct}()
281  * can be used to build a priority queue with N elements
282  * in time proportional to N.
283  *
284  * First create an empty queue with \Ref{pblPriorityQueueNew}()
285  * and ensure the queue has space for N elements via a
286  * call to \Ref{pblPriorityQueueEnsureCapacity}(),
287  * then add all elements via calls to this function
288  * and finally ensure the heap condition with a call
289  * to \Ref{pblPriorityQueueConstruct}().
290  *
291  * @return int rc >= 0: The size of the queue.
292  * @return int rc <  0: An error, see pbl_errno:
293  *
294  * <BR>PBL_ERROR_OUT_OF_MEMORY - Out of memory.
295  */
pblPriorityQueueAddLast(PblPriorityQueue * queue,int priority,void * element)296 int pblPriorityQueueAddLast( /*                       */
297 PblPriorityQueue * queue, /** The queue to use        */
298 int priority, /** Priority of the element to be added */
299 void * element /** Element to be added to the queue   */
300 )
301 {
302     int rc;
303     PblPriorityQueueEntry *newEntry =
304             (PblPriorityQueueEntry *)pbl_malloc( "pblPriorityQueueAddLast",
305                                                  sizeof(PblPriorityQueueEntry) );
306     if( !newEntry )
307     {
308         return -1;
309     }
310 
311     newEntry->element = element;
312     newEntry->priority = priority;
313 
314     rc = pblHeapAddLast( (PblHeap *)queue, newEntry );
315     if( rc < 0 )
316     {
317         PBL_FREE(newEntry);
318         return rc;
319     }
320 
321     return pblHeapSize( (PblHeap *)queue );
322 }
323 
324 /**
325  * Inserts the element with the specified priority into the
326  * priority queue and maintains the heap condition
327  * of the priority queue.
328  *
329  * This function has a time complexity of O(Log N),
330  * with N being the number of elements in the queue.
331  *
332  * @return int rc >= 0: The size of the queue.
333  * @return int rc <  0: An error, see pbl_errno:
334  *
335  * <BR>PBL_ERROR_OUT_OF_MEMORY - Out of memory.
336  */
pblPriorityQueueInsert(PblPriorityQueue * queue,int priority,void * element)337 int pblPriorityQueueInsert( /*                           */
338 PblPriorityQueue * queue, /** The queue to use           */
339 int priority, /** Priority of the element to be inserted */
340 void * element /** Element to be inserted to the queue   */
341 )
342 {
343     // Add to the end of the queue
344     //
345     int rc = pblPriorityQueueAddLast( queue, priority, element );
346     if( rc > 1 )
347     {
348         // Ensure the heap condition for the last entry
349         //
350         pblHeapEnsureCondition( queue, rc - 1 );
351     }
352     return rc;
353 }
354 
355 /**
356  * Removes the last element from the priority queue,
357  * maintaining the heap condition of the queue.
358  *
359  * <B>Note:</B> The last element is not guaranteed to be
360  * the element with the lowest priority!
361  *
362  * This function has a time complexity of O(1).
363  *
364  * @return void* retptr != (void*)-1: The element removed.
365  * @return void* retptr == (void*)-1: An error see pbl_errno:
366  *
367  * <BR>PBL_ERROR_OUT_OF_BOUNDS - The queue is empty.
368  */
pblPriorityQueueRemoveLast(PblPriorityQueue * queue,int * priority)369 void * pblPriorityQueueRemoveLast( /*                                     */
370 PblPriorityQueue * queue, /** The queue to use                            */
371 int * priority /** On return contains the priority of the element removed */
372 )
373 {
374     void * retptr;
375     PblPriorityQueueEntry *lastEntry =
376             (PblPriorityQueueEntry *)pblHeapRemoveLast( (PblHeap *)queue );
377 
378     if( lastEntry == (PblPriorityQueueEntry *)-1 )
379     {
380         return (void*)-1;
381     }
382 
383     retptr = lastEntry->element;
384     if( priority )
385     {
386         *priority = lastEntry->priority;
387     }
388 
389     PBL_FREE(lastEntry);
390 
391     // Removing the last entry cannot break the heap condition!
392     //
393     return retptr;
394 }
395 
396 /**
397  * Removes the element at the specified position from the
398  * priority queue, maintaining the heap condition of the queue.
399  *
400  * This function has a time complexity of O(Log N),
401  * with N being the number of elements in the queue.
402  *
403  * @return void* retptr != (void*)-1: The element removed.
404  * @return void* retptr == (void*)-1: An error see pbl_errno:
405  *
406  * <BR>PBL_ERROR_OUT_OF_BOUNDS - Index is out of range (index < 0 || index >= size()).
407  */
pblPriorityQueueRemoveAt(PblPriorityQueue * queue,int index,int * priority)408 void * pblPriorityQueueRemoveAt( /*                                       */
409 PblPriorityQueue * queue, /** The queue to use                            */
410 int index, /** The index at which the element is to be removed            */
411 int * priority /** On return contains the priority of the element removed */
412 )
413 {
414     void * retptr;
415     PblPriorityQueueEntry *entry = pblHeapRemoveAt( (PblHeap *)queue, index );
416     if( entry == (PblPriorityQueueEntry *)-1 )
417     {
418         return (void*)-1;
419     }
420 
421     retptr = entry->element;
422     if( priority )
423     {
424         *priority = entry->priority;
425     }
426 
427     PBL_FREE(entry);
428 
429     return retptr;
430 }
431 
432 /**
433  * Removes the element with the highest priority from the
434  * priority queue, maintaining the heap condition of the queue.
435  *
436  * This function has a time complexity of O(Log N),
437  * with N being the number of elements in the queue.
438  *
439  * @return void* retptr != (void*)-1: The element removed.
440  * @return void* retptr == (void*)-1: An error see pbl_errno:
441  *
442  * <BR>PBL_ERROR_OUT_OF_BOUNDS - The queue is empty.
443  */
pblPriorityQueueRemoveFirst(PblPriorityQueue * queue,int * priority)444 void * pblPriorityQueueRemoveFirst( /*                                    */
445 PblPriorityQueue * queue, /** The queue to use                            */
446 int * priority /** On return contains the priority of the element removed */
447 )
448 {
449     return pblPriorityQueueRemoveAt( queue, 0, priority );
450 }
451 
452 /**
453  * Returns the element at the specified position in the priority queue.
454  *
455  * This method has a time complexity of O(1).
456  *
457  * @return void * retptr != (void*)-1: The element at the specified position, may be NULL.
458  * @return void * retptr == (void*)-1: An error, see pbl_errno:
459  *
460  * <BR>PBL_ERROR_OUT_OF_BOUNDS - Index is out of range (index < 0 || index >= size()).
461  */
pblPriorityQueueGet(PblPriorityQueue * queue,int index,int * priority)462 void * pblPriorityQueueGet( /*                                    */
463 PblPriorityQueue * queue, /** The queue to use                    */
464 int index, /** Index of the element to return                     */
465 int * priority /** On return contains the priority of the element */
466 )
467 {
468     PblPriorityQueueEntry *entry =
469             (PblPriorityQueueEntry *)pblHeapGet( (PblHeap *)queue, index );
470 
471     if( entry == (PblPriorityQueueEntry *)-1 )
472     {
473         return (void*)-1;
474     }
475 
476     if( priority )
477     {
478         *priority = entry->priority;
479     }
480 
481     return entry->element;
482 }
483 
484 /**
485  * Returns but does not remove the element with the highest priority in the
486  * priority queue.
487  *
488  * This function has a time complexity of O(1).
489  *
490  * @return void* retptr != (void*)-1: The element returned.
491  * @return void* retptr == (void*)-1: An error see pbl_errno:
492  *
493  * <BR>PBL_ERROR_OUT_OF_BOUNDS - The queue is empty.
494  */
pblPriorityQueueGetFirst(PblPriorityQueue * queue,int * priority)495 void * pblPriorityQueueGetFirst( /*                                     */
496 PblPriorityQueue * queue, /** The queue to use                          */
497 int * priority /** On return contains the priority of the first element */
498 )
499 {
500     return pblPriorityQueueGet( queue, 0, priority );
501 }
502 
503 /**
504  * Constructs a priority queue using 'bottom-up heap construction'.
505  *
506  * This function has a time complexity of O(N),
507  * with N being the number of elements in the queue.
508  *
509  * This function together with \Ref{pblPriorityQueueAddLast}()
510  * can be used to build a priority queue with N elements
511  * in time proportional to N.
512  *
513  * First create an empty queue with \Ref{pblPriorityQueueNew}()
514  * and ensure the queue has space for N elements via a
515  * call to \Ref{pblPriorityQueueEnsureCapacity}(),
516  * then add all elements via calls to \Ref{pblPriorityQueueAddLast}(),
517  * and finally ensure the heap condition with a call
518  * to this function.
519  */
pblPriorityQueueConstruct(PblPriorityQueue * queue)520 void pblPriorityQueueConstruct( /*            */
521 PblPriorityQueue * queue /** The queue to use */
522 )
523 {
524     pblHeapConstruct( (PblHeap *)queue );
525 }
526 
527 /**
528  * Changes the priority of the element at the specified position of the
529  * priority queue, maintaining the heap condition of the queue.
530  *
531  * This function has a time complexity of O(Log N),
532  * with N being the number of elements in the queue.
533  *
534  * @return int rc >= 0: The index of the element after the priority change.
535  * @return int rc <  0: An error see pbl_errno:
536  *
537  * <BR>PBL_ERROR_OUT_OF_BOUNDS - Index is out of range (index < 0 || index >= size()).
538  */
pblPriorityQueueChangePriorityAt(PblPriorityQueue * queue,int index,int priority)539 int pblPriorityQueueChangePriorityAt( /*                        */
540 PblPriorityQueue * queue, /** The queue to use                  */
541 int index, /** The index at which the priority is to be changed */
542 int priority /** The new priority of the element                */
543 )
544 {
545     PblPriorityQueueEntry *entry =
546             (PblPriorityQueueEntry *)pblHeapGet( (PblHeap *)queue, index );
547     if( entry == (PblPriorityQueueEntry *)-1 )
548     {
549         return -1;
550     }
551 
552     if( priority < entry->priority )
553     {
554         int size = pblHeapSize( (PblHeap *)queue );
555 
556         entry->priority = priority;
557 
558         // For zero based arrays all entries with an index bigger
559         // than 'size / 2 - 1' do not have any children.
560         //
561         // Decreasing the priority cannot violate the heap condition
562         // for entries with no children
563         //
564         if( index <= size / 2 - 1 )
565         {
566             // The heap condition only needs to be ensured
567             // if the entry has children
568             //
569             return pblHeapEnsureCondition( queue, index );
570         }
571     }
572     else if( priority > entry->priority )
573     {
574         entry->priority = priority;
575 
576         // Increasing the priority cannot violate the heap condition
577         // for the top entry at index 0
578         //
579         if( index > 0 )
580         {
581             // The heap condition needs to ensured
582             //
583             return pblHeapEnsureCondition( queue, index );
584         }
585     }
586 
587     return index;
588 }
589 
590 /**
591  * Changes the priority of the first element of the priority queue,
592  * maintaining the heap condition of the queue.
593  *
594  * This function has a time complexity of O(Log N),
595  * with N being the number of elements in the queue.
596  *
597  * @return int rc >= 0: The index of the first element after the priority change.
598  * @return int rc <  0: An error see pbl_errno:
599  *
600  * <BR>PBL_ERROR_OUT_OF_BOUNDS - The queue is empty.
601  */
pblPriorityQueueChangePriorityFirst(PblPriorityQueue * queue,int priority)602 int pblPriorityQueueChangePriorityFirst( /*            */
603 PblPriorityQueue * queue, /** The queue to use         */
604 int priority /** The new priority of the first element */
605 )
606 {
607     return pblPriorityQueueChangePriorityAt( queue, 0, priority );
608 }
609 
610 /**
611  * Returns an iterator over the elements in the queue.
612  *
613  * The iterator starts the iteration at the element with the highest priority.
614  *
615  * <B>Note</B>: The memory allocated by this method for the iterator returned needs to be released
616  *              by calling \Ref{pblIteratorFree}() once the iterator is no longer needed.
617  *
618  * The pointers returned by the \Ref{pblIteratorNext}() or \Ref{pblIteratorPrevious}() functions
619  * of the iterator are of type \Ref{PblPriorityQueueEntry} allowing to access priority and
620  * element.
621  *
622  * Modifying the priority queue via the Iterator's own remove or add methods
623  * does not maintain the heap property of the priority queue.
624  * In this case the heap property has to be restored by a call
625  * to \Ref{pblPriorityQueueConstruct}().
626  *
627  * The iterators returned by the this method are fail-fast:
628  * if the queue is structurally modified at any time after the iterator is created,
629  * in any way except through the Iterator's own remove or add methods,
630  * the iterator will return a PBL_ERROR_CONCURRENT_MODIFICATION error.
631  *
632  * Thus, in the face of concurrent modification,
633  * the iterator fails quickly and cleanly,
634  * rather than risking arbitrary, non-deterministic
635  * behavior at an undetermined time in the future.
636  *
637  * This method has a time complexity of O(1).
638  *
639  * @return void * retptr != NULL: The iterator.
640  * @return void * retptr == NULL: An error, see pbl_errno:
641  *
642  * <BR>PBL_ERROR_OUT_OF_MEMORY - Out of memory.
643  */
pblPriorityQueueIterator(PblPriorityQueue * queue)644 PblIterator * pblPriorityQueueIterator( /*    */
645 PblPriorityQueue * queue /** The queue to use */
646 )
647 {
648     return pblHeapIterator( (PblHeap *)queue );
649 }
650 
651 /**
652  * Joins the two priority queues by moving all elements of the 'other'
653  * queue. When this function returns, 'other' will be empty.
654  *
655  * This function has a time complexity of O(N), with N
656  * being the number of elements in the queue after the join.
657  *
658  * @return int rc >= 0: The size of the queue after tjhe join.
659  * @return int rc <  0: An error, see pbl_errno:
660  *
661  * <BR>PBL_ERROR_OUT_OF_MEMORY - Out of memory.
662  */
pblPriorityQueueJoin(PblPriorityQueue * queue,PblPriorityQueue * other)663 int pblPriorityQueueJoin( /*                         */
664 PblPriorityQueue * queue, /** The queue to join to   */
665 PblPriorityQueue * other /** The other queue to join */
666 )
667 {
668     return pblHeapJoin( (PblHeap *)queue, (PblHeap *)other );
669 }
670