├── README.md ├── main.c ├── pqueue.c └── pqueue.h /README.md: -------------------------------------------------------------------------------- 1 | # Generic Priority Queue in C 2 | 3 | "Academical" implementation of a priority queue in old C, nothing fancy. 4 | 5 | I guess I (re)did this to keep my CS skills sharp. 6 | 7 | ### How it works. 8 | 9 | The "constructor" method expects a comparator method and the capacity of the Queue: 10 | 11 | ```C 12 | PQueue *pqueue_new(int (*cmp)(const void *d1, const void *d2), size_t capacity); 13 | ``` 14 | 15 | A simple comparator method for `int` values can look like: 16 | 17 | ```C 18 | int cmp_ints(const void *int1, const void *int2) { 19 | return *(int*) int1 - *(int*) int2; 20 | } 21 | ``` 22 | 23 | Once we have the comparator method we can create the `PQueue` and elements to it (in our case int numbers): 24 | 25 | ```C 26 | PQueue* pq = pqueue_new(cmp_ints, 200); 27 | 28 | int x = 100, y = 50, z = 300, k = 100, w = 1000; 29 | 30 | pqueue_enqueue(pq, &x); 31 | pqueue_enqueue(pq, &y); 32 | pqueue_enqueue(pq, &z); 33 | pqueue_enqueue(pq, &k); 34 | pqueue_enqueue(pq, &w); 35 | 36 | int i = 0; 37 | for(;i<5;++i) 38 | printf("%d\n", *(int*) pqueue_dequeue(pq)); 39 | 40 | // De-allocate the memory (PQueue only, not the elements) 41 | pqueue_delete(pq); 42 | ``` 43 | 44 | Output: 45 | 46 | ``` 47 | 1000 48 | 300 49 | 100 50 | 100 51 | 50 52 | ``` 53 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "pqueue.h" 5 | 6 | int cmp_ints(const void *int1, const void *int2) { 7 | return *(int*) int1 - *(int*) int2; 8 | } 9 | 10 | int main(int argc, char** argv) { 11 | 12 | PQueue* pq = pqueue_new(cmp_ints, 200); 13 | 14 | int x = 100, y = 50, z = 300, k = 100, w = 1000; 15 | 16 | pqueue_enqueue(pq, &x); 17 | pqueue_enqueue(pq, &y); 18 | pqueue_enqueue(pq, &z); 19 | pqueue_enqueue(pq, &k); 20 | pqueue_enqueue(pq, &w); 21 | 22 | int i = 0; 23 | for(;i<5;++i) 24 | printf("%d\n", *(int*) pqueue_dequeue(pq)); 25 | 26 | pqueue_delete(pq); 27 | 28 | return (EXIT_SUCCESS); 29 | } 30 | 31 | -------------------------------------------------------------------------------- /pqueue.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "pqueue.h" 5 | 6 | /* Util macros */ 7 | #define LEFT(x) (2 * (x) + 1) 8 | #define RIGHT(x) (2 * (x) + 2) 9 | #define PARENT(x) ((x) / 2) 10 | 11 | void pqueue_heapify(PQueue *q, size_t idx); 12 | 13 | /** 14 | * Allocates memory for a new Priority Queue structure . 15 | 16 | * 'cmp' function: 17 | * returns 0 if d1 and d2 have the same priorities 18 | * returns [negative value] if d1 have a smaller priority than d2 19 | * returns [positive value] if d1 have a greater priority than d2 20 | */ 21 | PQueue *pqueue_new(int (*cmp)(const void *d1, const void *d2), 22 | size_t capacity) { 23 | PQueue *res = NULL; 24 | NP_CHECK(cmp); 25 | res = malloc(sizeof(*res)); 26 | NP_CHECK(res); 27 | res->cmp = cmp; 28 | /* The inner representation of data inside the queue is an array of void* */ 29 | res->data = malloc(capacity * sizeof(*(res->data))); 30 | NP_CHECK(res->data); 31 | res->size = 0; 32 | res->capacity = capacity; 33 | return (res); 34 | } 35 | 36 | /** 37 | * De-allocates memory for a given Priority Queue structure . 38 | */ 39 | void pqueue_delete(PQueue *q) { 40 | if (NULL == q) { 41 | DEBUG("Priority Queue is already NULL. Nothing to free."); 42 | return; 43 | } 44 | free(q->data); 45 | free(q); 46 | } 47 | 48 | /** 49 | * Adds a new element to the Priority Queue . 50 | */ 51 | void pqueue_enqueue(PQueue *q, const void *data) { 52 | size_t i; 53 | void *tmp = NULL; 54 | NP_CHECK(q); 55 | if (q->size >= q->capacity) { 56 | DEBUG("Priority Queue is full. Cannot add another element ."); 57 | return; 58 | } 59 | /* Adds element last */ 60 | q->data[q->size] = (void*) data; 61 | i = q->size; 62 | q->size++; 63 | /* The new element is swapped with its parent as long as its 64 | precedence is higher */ 65 | while(i > 0 && q->cmp(q->data[i], q->data[PARENT(i)]) > 0) { 66 | tmp = q->data[i]; 67 | q->data[i] = q->data[PARENT(i)]; 68 | q->data[PARENT(i)] = tmp; 69 | i = PARENT(i); 70 | } 71 | } 72 | 73 | /** 74 | * Returns the element with the biggest priority from the queue . 75 | */ 76 | void *pqueue_dequeue(PQueue *q) { 77 | void *data = NULL; 78 | NP_CHECK(q); 79 | if (q->size < 1) { 80 | /* Priority Queue is empty */ 81 | DEBUG("Priority Queue underflow . Cannot remove another element ."); 82 | return NULL; 83 | } 84 | data = q->data[0]; 85 | q->data[0] = q->data[q->size-1]; 86 | q->size--; 87 | /* Restore heap property */ 88 | pqueue_heapify(q, 0); 89 | return (data); 90 | } 91 | 92 | /** 93 | * Turn an "almost-heap" into a heap . 94 | */ 95 | void pqueue_heapify(PQueue *q, size_t idx) { 96 | /* left index, right index, largest */ 97 | void *tmp = NULL; 98 | size_t l_idx, r_idx, lrg_idx; 99 | NP_CHECK(q); 100 | 101 | l_idx = LEFT(idx); 102 | r_idx = RIGHT(idx); 103 | 104 | /* Left child exists, compare left child with its parent */ 105 | if (l_idx < q->size && q->cmp(q->data[l_idx], q->data[idx]) > 0) { 106 | lrg_idx = l_idx; 107 | } else { 108 | lrg_idx = idx; 109 | } 110 | 111 | /* Right child exists, compare right child with the largest element */ 112 | if (r_idx < q->size && q->cmp(q->data[r_idx], q->data[lrg_idx]) > 0) { 113 | lrg_idx = r_idx; 114 | } 115 | 116 | /* At this point largest element was determined */ 117 | if (lrg_idx != idx) { 118 | /* Swap between the index at the largest element */ 119 | tmp = q->data[lrg_idx]; 120 | q->data[lrg_idx] = q->data[idx]; 121 | q->data[idx] = tmp; 122 | /* Heapify again */ 123 | pqueue_heapify(q, lrg_idx); 124 | } 125 | } -------------------------------------------------------------------------------- /pqueue.h: -------------------------------------------------------------------------------- 1 | #ifndef __PQUEUE__H__ 2 | #define __PQUEUE__H__ 3 | 4 | /** 5 | * Debugging macro . 6 | * 7 | * Checks for a NULL pointer, and prints the error message, source file and 8 | * line via 'stderr' . 9 | * If the check fails the program exits with error code (-1) . 10 | */ 11 | #define NP_CHECK(ptr) \ 12 | { \ 13 | if (NULL == (ptr)) { \ 14 | fprintf(stderr, "%s:%d NULL POINTER: %s n", \ 15 | __FILE__, __LINE__, #ptr); \ 16 | exit(-1); \ 17 | } \ 18 | } \ 19 | 20 | #define DEBUG(msg) fprintf(stderr, "%s:%d %s", __FILE__, __LINE__, (msg)) 21 | 22 | /** 23 | * Priority Queue Structure 24 | */ 25 | typedef struct PQueue_s { 26 | /* The actual size of heap at a certain time */ 27 | size_t size; 28 | /* The amount of allocated memory for the heap */ 29 | size_t capacity; 30 | /* An array of (void*), the actual max-heap */ 31 | void **data; 32 | /* A pointer to a comparator function, used to prioritize elements */ 33 | int (*cmp)(const void *d1, const void *d2); 34 | } PQueue; 35 | 36 | /** Allocates memory for a new Priority Queue . 37 | Needs a pointer to a comparator function, thus establishing priorities . 38 | */ 39 | PQueue *pqueue_new(int (*cmp)(const void *d1, const void *d2), 40 | size_t capacity); 41 | 42 | /** De-allocates memory for a given Priority Queue */ 43 | void pqueue_delete(PQueue *q); 44 | 45 | /** Add an element inside the Priority Queue */ 46 | void pqueue_enqueue(PQueue *q, const void *data); 47 | 48 | /** Removes the element with the greatest priority from within the Queue */ 49 | void *pqueue_dequeue(PQueue *q); 50 | 51 | #endif --------------------------------------------------------------------------------