Priority Queue
Written by Luka Kerr on August 13, 2018
The code for a binary heap based priority queue, written while at UNSW. Relies on an AVL tree.
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include "AVL.h"
// multiplier to resize an [] by, see https://cs.stackexchange.com/q/74350
#define MULTIPLER 2
#define MIN_ITEMS 100
/**
* @typedef {struct} PQ: a pointer to a binary heap
*/
typedef struct PQRep *PQ;
/**
* @typedef {struct} ItemPQ: a priority queue item
* @property {key} - the key of the item
* @property {value} - the value of the item
*/
typedef struct ItemPQ {
int key;
int value;
} ItemPQ;
/**
* @typedef {enum} Action: action to perform when reallocating
* @property {DECREASE} - when realloc'ing decrease the size
* @property {INCREASE} - when realloc'ing increase the size
*/
typedef enum {
DECREASE = 0,
INCREASE = 1
} Action;
/**
* @typedef {struct} PQRep: a binary heap
* @property {numI} - the number of actual items in the array
* @property {numE} - the number of possible slots in the array
* @property {ItemPQ *} - array of PQ items
* @property {AVL} - AVL tree to hold key and index of items
*/
typedef struct PQRep {
int numI;
int numS;
ItemPQ *items;
AVL keyTree;
} PQRep;
static bool reallocPQ(PQ q, Action action);
static void bubbleUp(PQ q, ItemPQ *items, int a);
static void bubbleDown(PQ q, ItemPQ *items, int a, int n);
static void swap(PQ q, ItemPQ *a, int b, int c);
static bool greater(ItemPQ a, ItemPQ b);
/**
* Create a new priority queue, that can store items of type ItemPQ
* @return {PQ} The newly created priority queue
*/
PQ newPQ() {
PQRep *q;
q = malloc(sizeof(PQRep));
assert(q != NULL);
q->items = malloc(sizeof(ItemPQ) * (MIN_ITEMS + 1));
assert(q->items != NULL);
q->numI = 0;
q->numS = MIN_ITEMS;
q->keyTree = newAVL();
return q;
}
/**
* Adds an item to the priority queue. If an item with the key already
* exists, then its value is just updated
* @param {PQ} q The queue to operate on
* @param {ItemPQ} element The item to add
* @return {void}
*/
void addPQ(PQ q, ItemPQ element) {
assert(q != NULL);
// trying to add but not enough space in array, so make space
if (q->numI >= q->numS) {
assert(reallocPQ(q, INCREASE));
}
// check if item with key exists
int found = searchAVL(q->keyTree, element.key);
if (found != -1) {
q->items[found].value = element.value;
bubbleUp(q, q->items, found);
return;
}
q->numI++;
q->items[q->numI] = element;
q->keyTree = insertAVL(q->keyTree, element.key, q->numI);
bubbleUp(q, q->items, q->numI);
}
/**
* Removes and returns the item with the smallest value. For items with
* equal value, follow FIFO. If the queue is empty, return NULL
* @param {PQ} q The queue to operate on
* @return {ItemPQ} The item with the smallest value
*/
ItemPQ dequeuePQ(PQ q) {
assert(!PQEmpty(q));
swap(q, q->items, 1, q->numI);
q->numI--;
bubbleDown(q, q->items, 1, q->numI);
q->keyTree = deleteAVL(q->keyTree, q->items[q->numI + 1].key);
return q->items[q->numI + 1];
}
/**
* Updates the item with a given key, by updating that item's value to the
* given value. If an item with the given key does not exists in the queue,
* do nothing
* @param {PQ} q The queue to operate on
* @param {ItemPQ} element The item to update
* @return {void}
*/
void updatePQ(PQ q, ItemPQ element) {
assert(q != NULL);
int found = searchAVL(q->keyTree, element.key);
if (found != -1) {
int oldValue = q->items[found].value;
q->items[found].value = element.value;
if (oldValue > element.value) {
bubbleUp(q, q->items, found);
} else if (oldValue < element.value) {
bubbleDown(q, q->items, found, q->numI);
}
}
}
/**
* Returns an int (1 or 0) for whether the queue is empty
* @param {PQ} q The priority queue to check
* @return {int} The 1 if empty, 0 if not
*/
int PQEmpty(PQ q) {
assert(q != NULL);
return q->numI <= 0;
}
/**
* Frees all the memory from a given priority queue
* @param {PQ} q The queue to free
* @return {void}
*/
void freePQ(PQ q) {
assert(q != NULL);
freeAVL(q->keyTree);
free(q->items);
free(q);
}
/**
* Increase or decrease space in an items array in a priority queue
* @param {PQ} q The queue to realloc
* @param {Action} a The action to perform (DECREASE or INCREASE)
* @return {bool} Whether the realloc was successful
*/
static bool reallocPQ(PQ q, Action a) {
int n = q->numS;
q->numS = a ? (n * MULTIPLER) : (n / MULTIPLER);
n = q->numS;
int size = a ? (n * MULTIPLER) : (n / MULTIPLER);
q->items = realloc(q->items, size * sizeof(ItemPQ));
return q->items != NULL;
}
/**
* Moves the item at items[a] into the correct position in the queue
* @param {PQ} q The priority queue
* @param {ItemPQ *} items The array of items in the queue
* @param {int} a The item index to correct
* @return {void}
*/
static void bubbleUp(PQ q, ItemPQ *items, int a) {
while (a > 1 && greater(items[a / 2], items[a])) {
swap(q, items, a, a / 2);
a = a / 2;
}
}
/**
* Moves the item at items[a] into the correct position in the queue
* @param {PQ} q The priority queue
* @param {ItemPQ *} items The array of items in the queue
* @param {int} a The item index to correct
* @param {int} n The max index and number of items in the queue
* @return {void}
*/
static void bubbleDown(PQ q, ItemPQ *items, int a, int n) {
while (2 * a <= n) {
// compute address of left child
int j = 2 * a;
// choose larger of two children
if (j < n && greater(items[j], items[j + 1])) {
j++;
}
if (!greater(items[a], items[j])) {
break;
}
swap(q, items, a, j);
// move one level down the heap
a = j;
}
}
/**
* Swap two indexes in an array
* @param {PQ} q The priority queue
* @param {ItemPQ *} items The array of items in the queue
* @param {int} b An index to swap
* @param {int} c An index to swap
* @return {void}
*/
static void swap(PQ q, ItemPQ *items, int b, int c) {
swapAVL(q->keyTree, items[b].key, items[c].key);
ItemPQ tmp = items[b];
items[b] = items[c];
items[c] = tmp;
}
/**
* Returns whether the first passed item is greater than the second
* @param {ItemPQ} a The first item
* @param {ItemPQ} b The second item
* @return {bool} Whether a > b
*/
static bool greater(ItemPQ a, ItemPQ b) {
return a.value > b.value;
}