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