mirror of
https://github.com/bkthomps/Containers.git
synced 2025-12-05 23:23:24 +00:00
Add priority_queue
This commit is contained in:
226
src/priority_queue.c
Normal file
226
src/priority_queue.c
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017-2018 Bailey Thompson
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <memory.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "vector.h"
|
||||||
|
#include "priority_queue.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Must exactly match the declaration in vector.c
|
||||||
|
*/
|
||||||
|
struct _vector {
|
||||||
|
size_t data_size;
|
||||||
|
int offset;
|
||||||
|
int space;
|
||||||
|
void *storage;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _priority_queue {
|
||||||
|
vector data;
|
||||||
|
size_t data_size;
|
||||||
|
int (*comparator)(const void *const one, const void *const two);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a priority queue, which adapts a container to provide priority
|
||||||
|
* queue. Adapts the vector container.
|
||||||
|
*
|
||||||
|
* @param data_size The size of the data in the priority queue.
|
||||||
|
* @param comparator The priority comparator function.
|
||||||
|
*
|
||||||
|
* @return The newly-initialized priority queue, or NULL if memory allocation
|
||||||
|
* error.
|
||||||
|
*/
|
||||||
|
priority_queue priority_queue_init(const size_t data_size,
|
||||||
|
int (*comparator)(const void *const,
|
||||||
|
const void *const))
|
||||||
|
{
|
||||||
|
struct _priority_queue *const init = malloc(sizeof(struct _priority_queue));
|
||||||
|
if (init == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
init->data_size = data_size;
|
||||||
|
init->data = vector_init(data_size);
|
||||||
|
if (init->data == NULL) {
|
||||||
|
free(init);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
init->comparator = comparator;
|
||||||
|
return init;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the size of the priority queue.
|
||||||
|
*
|
||||||
|
* @param me The priority queue to check.
|
||||||
|
*
|
||||||
|
* @return The size of the priority queue.
|
||||||
|
*/
|
||||||
|
int priority_queue_size(priority_queue me)
|
||||||
|
{
|
||||||
|
return vector_size(me->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether or not the priority queue is empty.
|
||||||
|
*
|
||||||
|
* @param me The priority queue to check.
|
||||||
|
*
|
||||||
|
* @return If the priority queue is empty.
|
||||||
|
*/
|
||||||
|
bool priority_queue_is_empty(priority_queue me)
|
||||||
|
{
|
||||||
|
return vector_is_empty(me->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an element to the priority queue.
|
||||||
|
*
|
||||||
|
* @param me The priority queue to add an element to.
|
||||||
|
* @param data The data to add to the queue.
|
||||||
|
*
|
||||||
|
* @return 0 No error.
|
||||||
|
* -ENOMEM Out of memory.
|
||||||
|
*/
|
||||||
|
int priority_queue_push(priority_queue me, void *const data)
|
||||||
|
{
|
||||||
|
void *const temp = malloc(me->data_size);
|
||||||
|
if (temp == NULL) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
const int rc = vector_add_last(me->data, data);
|
||||||
|
if (rc != 0) {
|
||||||
|
free(temp);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
void *const vector_storage = me->data->storage;
|
||||||
|
int index = vector_size(me->data) - 1;
|
||||||
|
int parent_index = (index - 1) / 2;
|
||||||
|
void *data_index = vector_storage + index * me->data_size;
|
||||||
|
void *data_parent_index = vector_storage + parent_index * me->data_size;
|
||||||
|
while (index > 0 && me->comparator(data_index, data_parent_index) > 0) {
|
||||||
|
memcpy(temp, data_parent_index, me->data_size);
|
||||||
|
memcpy(data_parent_index, data_index, me->data_size);
|
||||||
|
memcpy(data_index, temp, me->data_size);
|
||||||
|
index = parent_index;
|
||||||
|
parent_index = (index - 1) / 2;
|
||||||
|
data_index = vector_storage + index * me->data_size;
|
||||||
|
data_parent_index = vector_storage + parent_index * me->data_size;
|
||||||
|
}
|
||||||
|
free(temp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the highest priority element from the priority queue.
|
||||||
|
*
|
||||||
|
* @param data The data to have copied from the priority queue.
|
||||||
|
* @param me The priority queue to pop the next element from.
|
||||||
|
*
|
||||||
|
* @return If the priority queue contained elements.
|
||||||
|
*/
|
||||||
|
bool priority_queue_pop(void *const data, priority_queue me)
|
||||||
|
{
|
||||||
|
const int rc = vector_get_first(data, me->data);
|
||||||
|
if (rc != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
void *const vector_storage = me->data->storage;
|
||||||
|
const int size = vector_size(me->data) - 1;
|
||||||
|
void *const temp = vector_storage + size * me->data_size;
|
||||||
|
memcpy(vector_storage, temp, me->data_size);
|
||||||
|
int index = 0;
|
||||||
|
int left_index = 1;
|
||||||
|
int right_index = 2;
|
||||||
|
void *data_index = vector_storage;
|
||||||
|
void *data_left_index = vector_storage + left_index * me->data_size;
|
||||||
|
void *data_right_index = vector_storage + right_index * me->data_size;
|
||||||
|
while (true) {
|
||||||
|
if (right_index < size &&
|
||||||
|
me->comparator(data_right_index, data_left_index) > 0 &&
|
||||||
|
me->comparator(data_right_index, data_index) > 0) {
|
||||||
|
// Swap parent and right child then continue down right child.
|
||||||
|
memcpy(temp, data_index, me->data_size);
|
||||||
|
memcpy(data_index, data_right_index, me->data_size);
|
||||||
|
memcpy(data_right_index, temp, me->data_size);
|
||||||
|
index = right_index;
|
||||||
|
} else if (left_index < size &&
|
||||||
|
me->comparator(data_left_index, data_index) > 0) {
|
||||||
|
// Swap parent and left child then continue down left child.
|
||||||
|
memcpy(temp, data_index, me->data_size);
|
||||||
|
memcpy(data_index, data_left_index, me->data_size);
|
||||||
|
memcpy(data_left_index, temp, me->data_size);
|
||||||
|
index = left_index;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
left_index = 2 * index + 1;
|
||||||
|
right_index = 2 * index + 2;
|
||||||
|
data_index = vector_storage + index * me->data_size;
|
||||||
|
data_left_index = vector_storage + left_index * me->data_size;
|
||||||
|
data_right_index = vector_storage + right_index * me->data_size;
|
||||||
|
}
|
||||||
|
vector_remove_last(me->data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the highest priority element in the priority queue.
|
||||||
|
*
|
||||||
|
* @param data Out copy of the highest priority element in the priority queue.
|
||||||
|
* @param me The priority queue to copy from.
|
||||||
|
*
|
||||||
|
* @return If the priority queue contained elements.
|
||||||
|
*/
|
||||||
|
bool priority_queue_front(void *const data, priority_queue me)
|
||||||
|
{
|
||||||
|
return vector_get_first(data, me->data) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the elements from the priority queue.
|
||||||
|
*
|
||||||
|
* @param me The priority queue to clear.
|
||||||
|
*
|
||||||
|
* @return 0 No error.
|
||||||
|
* -ENOMEM Out of memory.
|
||||||
|
*/
|
||||||
|
int priority_queue_clear(priority_queue me)
|
||||||
|
{
|
||||||
|
return vector_clear(me->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees the priority queue memory.
|
||||||
|
*
|
||||||
|
* @param me The priority queue to free from memory.
|
||||||
|
*
|
||||||
|
* @return NULL
|
||||||
|
*/
|
||||||
|
priority_queue priority_queue_destroy(priority_queue me)
|
||||||
|
{
|
||||||
|
vector_destroy(me->data);
|
||||||
|
free(me);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
52
src/priority_queue.h
Normal file
52
src/priority_queue.h
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017-2018 Bailey Thompson
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CONTAINERS_PRIORITY_QUEUE_H
|
||||||
|
#define CONTAINERS_PRIORITY_QUEUE_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef struct _priority_queue *priority_queue;
|
||||||
|
|
||||||
|
// Starting
|
||||||
|
priority_queue priority_queue_init(size_t data_size,
|
||||||
|
int (*comparator)(const void *const one,
|
||||||
|
const void *const two));
|
||||||
|
|
||||||
|
// Utility
|
||||||
|
int priority_queue_size(priority_queue me);
|
||||||
|
bool priority_queue_is_empty(priority_queue me);
|
||||||
|
|
||||||
|
// Adding
|
||||||
|
int priority_queue_push(priority_queue me, void *data);
|
||||||
|
|
||||||
|
// Removing
|
||||||
|
bool priority_queue_pop(void *data, priority_queue me);
|
||||||
|
|
||||||
|
// Getting
|
||||||
|
bool priority_queue_front(void *data, priority_queue me);
|
||||||
|
|
||||||
|
// Ending
|
||||||
|
int priority_queue_clear(priority_queue me);
|
||||||
|
priority_queue priority_queue_destroy(priority_queue me);
|
||||||
|
|
||||||
|
#endif /* CONTAINERS_PRIORITY_QUEUE_H */
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2017 Bailey Thompson
|
* Copyright (c) 2017-2018 Bailey Thompson
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -32,8 +32,8 @@ struct _queue {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a queue which is first-in first-out. Uses a deque as the
|
* Initializes a queue, which adapts a container to provide queue
|
||||||
* underlying implementation.
|
* (first-in first-out). Adapts the deque container.
|
||||||
*
|
*
|
||||||
* @param data_size The size of each element.
|
* @param data_size The size of each element.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2017 Bailey Thompson
|
* Copyright (c) 2017-2018 Bailey Thompson
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -29,8 +29,8 @@ struct _stack {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a stack which is last-in first-out. Uses a deque as the
|
* Initializes a stack, which adapts a container to provide stack
|
||||||
* underlying implementation.
|
* (last-in first-out). Adapts the deque container.
|
||||||
*
|
*
|
||||||
* @param data_size The size of each data element in the stack.
|
* @param data_size The size of each data element in the stack.
|
||||||
*
|
*
|
||||||
|
|||||||
131
tst/priority_queue.c
Normal file
131
tst/priority_queue.c
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
#include "test.h"
|
||||||
|
#include "../src/vector.h"
|
||||||
|
#include "../src/priority_queue.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Include this for the stubs.
|
||||||
|
*/
|
||||||
|
struct _vector {
|
||||||
|
size_t data_size;
|
||||||
|
int offset;
|
||||||
|
int space;
|
||||||
|
void *storage;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Include this for the stubs.
|
||||||
|
*/
|
||||||
|
struct _priority_queue {
|
||||||
|
vector data;
|
||||||
|
size_t data_size;
|
||||||
|
int (*comparator)(const void *const one, const void *const two);
|
||||||
|
};
|
||||||
|
|
||||||
|
static void priority_queue_verify(priority_queue me)
|
||||||
|
{
|
||||||
|
void *const vector_storage = me->data->storage;
|
||||||
|
const int size = vector_size(me->data);
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
const int val = *(int *) (vector_storage + i * me->data_size);
|
||||||
|
const int left_child = 2 * i + 1;
|
||||||
|
const int right_child = 2 * i + 2;
|
||||||
|
if (left_child < size) {
|
||||||
|
void *left_data = vector_storage + left_child * me->data_size;
|
||||||
|
const int left_val = *(int *) left_data;
|
||||||
|
assert(val >= left_val);
|
||||||
|
}
|
||||||
|
if (right_child < size) {
|
||||||
|
void *right_data = vector_storage + right_child * me->data_size;
|
||||||
|
const int right_val = *(int *) right_data;
|
||||||
|
assert(val >= right_val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int compare_int(const void *const one, const void *const two)
|
||||||
|
{
|
||||||
|
const int a = *(int *) one;
|
||||||
|
const int b = *(int *) two;
|
||||||
|
return a - b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int stub_priority_queue_push(priority_queue me, void *const data)
|
||||||
|
{
|
||||||
|
const int ret = priority_queue_push(me, data);
|
||||||
|
priority_queue_verify(me);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool stub_priority_queue_pop(void *const data, priority_queue me)
|
||||||
|
{
|
||||||
|
const bool ret = priority_queue_pop(data, me);
|
||||||
|
priority_queue_verify(me);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_priority_queue(void)
|
||||||
|
{
|
||||||
|
priority_queue a = priority_queue_init(sizeof(int), compare_int);
|
||||||
|
assert(a != NULL);
|
||||||
|
assert(priority_queue_size(a) == 0);
|
||||||
|
assert(priority_queue_is_empty(a));
|
||||||
|
int b = 5;
|
||||||
|
stub_priority_queue_push(a, &b);
|
||||||
|
b = 2;
|
||||||
|
stub_priority_queue_push(a, &b);
|
||||||
|
b = 7;
|
||||||
|
stub_priority_queue_push(a, &b);
|
||||||
|
b = 3;
|
||||||
|
stub_priority_queue_push(a, &b);
|
||||||
|
b = 4;
|
||||||
|
stub_priority_queue_push(a, &b);
|
||||||
|
b = 5;
|
||||||
|
stub_priority_queue_push(a, &b);
|
||||||
|
b = 9;
|
||||||
|
stub_priority_queue_push(a, &b);
|
||||||
|
b = 2;
|
||||||
|
stub_priority_queue_push(a, &b);
|
||||||
|
b = 3;
|
||||||
|
stub_priority_queue_push(a, &b);
|
||||||
|
b = -5;
|
||||||
|
stub_priority_queue_push(a, &b);
|
||||||
|
b = 7;
|
||||||
|
stub_priority_queue_push(a, &b);
|
||||||
|
b = 3;
|
||||||
|
stub_priority_queue_push(a, &b);
|
||||||
|
b = 4;
|
||||||
|
stub_priority_queue_push(a, &b);
|
||||||
|
b = 3;
|
||||||
|
stub_priority_queue_push(a, &b);
|
||||||
|
b = 11;
|
||||||
|
stub_priority_queue_push(a, &b);
|
||||||
|
b = 6;
|
||||||
|
stub_priority_queue_push(a, &b);
|
||||||
|
assert(priority_queue_size(a) == 16);
|
||||||
|
assert(!priority_queue_is_empty(a));
|
||||||
|
b = 0xdeadbeef;
|
||||||
|
priority_queue_front(&b, a);
|
||||||
|
assert(b == 11);
|
||||||
|
b = 0xdeadbeef;
|
||||||
|
stub_priority_queue_pop(&b, a);
|
||||||
|
assert(b == 11);
|
||||||
|
assert(priority_queue_size(a) == 15);
|
||||||
|
b = 0xdeadbeef;
|
||||||
|
priority_queue_front(&b, a);
|
||||||
|
assert(b == 9);
|
||||||
|
int c = b;
|
||||||
|
for (int i = 0; i < 15; i++) {
|
||||||
|
stub_priority_queue_pop(&b, a);
|
||||||
|
assert(b <= c);
|
||||||
|
c = b;
|
||||||
|
assert(priority_queue_size(a) == 15 - i - 1);
|
||||||
|
}
|
||||||
|
priority_queue_clear(a);
|
||||||
|
assert(priority_queue_is_empty(a));
|
||||||
|
b = 0xdeadbeef;
|
||||||
|
assert(!priority_queue_front(&b, a));
|
||||||
|
assert(b == 0xdeadbeef);
|
||||||
|
assert(priority_queue_is_empty(a));
|
||||||
|
a = priority_queue_destroy(a);
|
||||||
|
assert(a == NULL);
|
||||||
|
}
|
||||||
@@ -17,5 +17,6 @@ int main(void)
|
|||||||
test_unordered_multimap();
|
test_unordered_multimap();
|
||||||
test_stack();
|
test_stack();
|
||||||
test_queue();
|
test_queue();
|
||||||
|
test_priority_queue();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,5 +20,6 @@ void test_unordered_multiset(void);
|
|||||||
void test_unordered_multimap(void);
|
void test_unordered_multimap(void);
|
||||||
void test_stack(void);
|
void test_stack(void);
|
||||||
void test_queue(void);
|
void test_queue(void);
|
||||||
|
void test_priority_queue(void);
|
||||||
|
|
||||||
#endif /* CONTAINERS_TEST_H */
|
#endif /* CONTAINERS_TEST_H */
|
||||||
|
|||||||
Reference in New Issue
Block a user