From 6b52d4d9cdfa8e3ff10a354d05966174c61d4e8e Mon Sep 17 00:00:00 2001 From: Camden Dixie O'Brien Date: Tue, 29 Oct 2024 16:02:59 +0000 Subject: [PATCH] Implement min heap --- lib/CMakeLists.txt | 1 + lib/include/min_heap.h | 12 ++++++++++ lib/min_heap.c | 53 ++++++++++++++++++++++++++++++++++++++++++ tests/CMakeLists.txt | 1 + tests/min_heap_tests.c | 49 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 116 insertions(+) create mode 100644 lib/include/min_heap.h create mode 100644 lib/min_heap.c create mode 100644 tests/min_heap_tests.c diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index d83b1e2..1707e23 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -2,6 +2,7 @@ add_library(lib construct.c desugar.c fsa.c + min_heap.c parse.c regex.c ) diff --git a/lib/include/min_heap.h b/lib/include/min_heap.h new file mode 100644 index 0000000..c7f94e6 --- /dev/null +++ b/lib/include/min_heap.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) Camden Dixie O'Brien + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#ifndef MIN_HEAP_H +#define MIN_HEAP_H + +void min_heap_heapify(int *xs, int count); +int min_heap_pop(int *xs, int *count); + +#endif diff --git a/lib/min_heap.c b/lib/min_heap.c new file mode 100644 index 0000000..7e4c4f9 --- /dev/null +++ b/lib/min_heap.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) Camden Dixie O'Brien + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#include "min_heap.h" + +static inline int left(int i) +{ + return 2 * i + 1; +} + +static inline int parent(int i) +{ + return (i - 1) / 2; +} + +static inline void swap(int *xs, int a, int b) +{ + int tmp = xs[a]; + xs[a] = xs[b]; + xs[b] = tmp; +} + +static void sift_down(int *xs, int root, int count) +{ + int child; + while ((child = left(root)) < count) { + if (child + 1 < count && xs[child] > xs[child + 1]) + ++child; + if (xs[root] > xs[child]) { + swap(xs, root, child); + root = child; + } else { + return; + } + } +} + +void min_heap_heapify(int *xs, int count) +{ + for (int i = parent(count - 1); i >= 0; --i) + sift_down(xs, i, count); +} + +int min_heap_pop(int *xs, int *count) +{ + int min = xs[0]; + --(*count); + xs[0] = xs[*count]; + sift_down(xs, 0, *count); + return min; +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 946ba70..7751e97 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -20,5 +20,6 @@ add_test_suites( construct_tests.c desugar_tests.c fsa_tests.c + min_heap_tests.c parse_tests.c ) diff --git a/tests/min_heap_tests.c b/tests/min_heap_tests.c new file mode 100644 index 0000000..127a4fb --- /dev/null +++ b/tests/min_heap_tests.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) Camden Dixie O'Brien + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#include "min_heap.h" +#include "testing.h" + +#include + +static bool is_min_heap(int *xs, int count) +{ + for (int i = 0; i < count; ++i) { + const int left = 2 * i + 1; + const int right = 2 * i + 2; + if (left < count && xs[left] < xs[i]) + return false; + if (right < count && xs[right] < xs[i]) + return false; + } + return true; +} + +static void array_is_min_heap_after_heapify(void) +{ + int xs[] = { 54, 12, 35, 43, 21, 12, 34, 52, 34, 23 }; + const int len = sizeof(xs) / sizeof(int); + min_heap_heapify(xs, len); + ASSERT_TRUE(is_min_heap(xs, len)); +} + +static void extract_root_yields_min(void) +{ + int xs[] = { 71, 31, 12, 21, 65, 53, 54, 10 }; + int len = 8; + min_heap_heapify(xs, len); + ASSERT_EQ(10, min_heap_pop(xs, &len)); + ASSERT_EQ(12, min_heap_pop(xs, &len)); + ASSERT_EQ(21, min_heap_pop(xs, &len)); + ASSERT_EQ(5, len); +} + +int main(void) +{ + TESTING_BEGIN(); + array_is_min_heap_after_heapify(); + extract_root_yields_min(); + return TESTING_END(); +}