Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dttools: priority queue data structure #3915

Closed
183 changes: 183 additions & 0 deletions dttools/src/priority_queue.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/*
Copyright (C) 2024 The University of Notre Dame
This software is distributed under the GNU General Public License.
See the file COPYING for details.
*/

#include "priority_queue.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

#define DEFAULT_CAPACITY 127
#define MAX_PRIORITY DBL_MAX

struct element {
void *data;
double priority;
};

struct priority_queue {
int size;
int capacity;
struct element **elements;
};

struct priority_queue *priority_queue_create(double init_capacity) {
struct priority_queue *pq = (struct priority_queue *)malloc(sizeof(struct priority_queue));
if (!pq)
return NULL;

if (init_capacity < 1) {
init_capacity = DEFAULT_CAPACITY;
}

pq->elements = (struct element **)calloc(init_capacity + 1, sizeof(struct element *));
if (!pq->elements) {
free(pq);
return NULL;
}

pq->capacity = init_capacity;
pq->size = 0;

pq->elements[0] = (struct element *)calloc(1, sizeof(struct element));
if (!pq->elements[0]) {
free(pq->elements);
free(pq);
return NULL;
}

pq->elements[0]->priority = MAX_PRIORITY;

return pq;
}

int priority_queue_size(struct priority_queue *pq) {
if (!pq) return -1;

return pq->size;
}

void swap_elements(struct priority_queue *pq, int i, int j) {
struct element *temp = pq->elements[i];
pq->elements[i] = pq->elements[j];
pq->elements[j] = temp;
}

void swim(struct priority_queue *pq, int k) {
if (!pq) return;

while (k > 1 && pq->elements[k / 2]->priority < pq->elements[k]->priority) {
swap_elements(pq, k, k / 2);
k /= 2;
}
}

void sink(struct priority_queue *pq, int k) {
if (!pq) return;

while (2 * k <= pq->size) {
int j = 2 * k;
if (j < pq->size && pq->elements[j]->priority < pq->elements[j + 1]->priority) {
j++;
}
if (pq->elements[k]->priority >= pq->elements[j]->priority) {
break;
}
swap_elements(pq, k, j);
k = j;
}
}

int priority_queue_double_capacity(struct priority_queue *pq) {
if (!pq) return 0;

int new_capacity = pq->capacity * 2;
struct element **new_elements = (struct element **)malloc(sizeof(struct element *) * (new_capacity + 1));
if (!new_elements) {
return 0;
}

memcpy(new_elements, pq->elements, sizeof(struct element *) * (pq->size + 1));

free(pq->elements);
pq->elements = new_elements;
pq->capacity = new_capacity;

return 1;
}

int priority_queue_push(struct priority_queue *pq, void *data, int priority) {
if (!pq) return 0;

if (pq->size >= pq->capacity) {
if (!priority_queue_double_capacity(pq)) {
return 0;
}
}
struct element *e = (struct element *)malloc(sizeof(struct element));
if (!e) {
return 0;
}
e->data = data;
e->priority = priority;

pq->elements[++pq->size] = e;
swim(pq, pq->size);

return 1;
}

void *priority_queue_pop(struct priority_queue *pq) {
if (!pq || pq->size == 0) return NULL;

struct element *e = pq->elements[1];
void *data = e->data;
pq->elements[1] = pq->elements[pq->size];
pq->elements[pq->size--] = NULL;
sink(pq, 1);
free(e);

return data;
}

void *priority_queue_get_head(struct priority_queue *pq) {
if (!pq || pq->size == 0) return NULL;

return pq->elements[1]->data;
}

void *priority_queue_get_element(struct priority_queue *pq, int index) {
if (!pq || pq->size < 1 || index < 1 || index > pq->size) return NULL;

return pq->elements[index]->data;
}

int priority_queue_remove(struct priority_queue *pq, void *data) {
if (!pq) return 0;

for (int i = 1; i <= pq->size; i++) {
if (pq->elements[i]->data == data) {
struct element *e = pq->elements[i];
pq->elements[i] = pq->elements[pq->size];
pq->elements[pq->size--] = NULL;
sink(pq, i);
free(e);
return 1;
}
}
return 0;
}

void priority_queue_destroy(struct priority_queue *pq) {
if (!pq) return;

for (int i = 0; i <= pq->size; i++) {
free(pq->elements[i]);
}
free(pq->elements);
free(pq);
}
121 changes: 121 additions & 0 deletions dttools/src/priority_queue.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
Copyright (C) 2024 The University of Notre Dame
This software is distributed under the GNU General Public License.
See the file COPYING for details.
*/

#ifndef PRIORITY_QUEUE_H
#define PRIORITY_QUEUE_H

/**
@file priority_queue.h
A general purpose priority queue.
This priority queue module is implemented as a complete binary heap that manages elements with associated priorities.
Each element in the priority queue has a priority, and the queue ensures that the element with the highest priority
can be accessed in constant time.
JinZhou5042 marked this conversation as resolved.
Show resolved Hide resolved

For example, to create a priority queue and manipulate its elements:
<pre>
struct priority_queue *pq;
pq = priority_queue_create(10);

int priority = 5;
void *data = someDataPointer;

priority_queue_push(pq, data, priority);
data = priority_queue_pop(pq);
void *headData = priority_queue_get_head(pq);
</pre>

To list all of the items in a priority queue, use a simple loop:
<pre>
for (int i = 1; i <= priority_queue_size(pq); i++) {
void *data = priority_queue_get_element(pq, i);
printf("Priority queue contains: %p\n", data);
}
</pre>

Or use the PRIORITY_QUEUE_ITERATE macro:

<pre>
void *data;
PRIORITY_QUEUE_ITERATE (pq, data) {
printf("Priority queue contains: %p\n", data);
}
</pre>
*/


/** Create a new priority queue.
The 0th element is used as a sentinel with the highest priority to simplify boundary checks in heap operations like swim and sink.
JinZhou5042 marked this conversation as resolved.
Show resolved Hide resolved
Element with a higher priority is at the top of the heap.
@param init_capacity The initial number of elements in the queue. If zero, a default value will be used.
@return A pointer to a new priority queue.
*/
struct priority_queue *priority_queue_create(int init_capacity);

/** Count the elements in a priority queue.
@param pq A pointer to a priority queue.
@return The number of elements in the queue.
*/
int priority_queue_size(struct priority_queue *pq);

/** Push an element into a priority queue.
function @ref swim is called to maintain the heap property.
JinZhou5042 marked this conversation as resolved.
Show resolved Hide resolved
@param pq A pointer to a priority queue.
@param data A pointer to store in the queue.
@param priority The specified priority with the given object.
@return One if the push succeeded, failure otherwise
*/
int priority_queue_push(struct priority_queue *pq, void *data, double priority);

/** Pop the element with the highest priority from a priority queue.
function @ref sink is called to maintain the heap property.
@param pq A pointer to a priority queue.
@return The pointer to the top of the heap if any, failure otherwise
*/
void *priority_queue_pop(struct priority_queue *pq);

/** Get the element with the highest priority from a priority queue.
Similar to @ref priority_queue_pop, but the element is not removed.
@param pq A pointer to a priority queue.
@return The pointer to the top of the heap if any, failure otherwise
*/
void *priority_queue_get_head(struct priority_queue *pq);

/** Get the element with the specified index from a priority queue.
@param pq A pointer to a priority queue.
@param index The index of the element to get.
@return The pointer to the element if any, failure otherwise
*/
void *priority_queue_get_element(struct priority_queue *pq, int index);

/** Remove the element with the specified index from a priority queue.
function @ref sink is called to maintain the heap property.
@param pq A pointer to a priority queue.
@param index The index of the element to remove.
@return One if the remove succeeded, failure otherwise
*/
int priority_queue_remove(struct priority_queue *pq, void *data);

/** Destroy a priority queue.
@param pq A pointer to a priority queue.
*/
void priority_queue_destroy(struct priority_queue *pq);

/** Utility macro to simplify common case of iterating over a priority queue.
Use as follows:

<pre>
char *data;

PRIORITY_QUEUE_ITERATE(pq, data) {
printf("data: %s\n", data);
}

</pre>
*/
#define PRIORITY_QUEUE_ITERATE( pq, data ) int i = 1; while ((data = priority_queue_get_element(pq, i++)))


#endif
Loading