diff options
author | Idan Horowitz <idan.horowitz@gmail.com> | 2021-03-03 23:05:34 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-03-13 20:07:25 +0100 |
commit | 168cf6fac9c9796884480bd4e98a6a37983c8f3d (patch) | |
tree | 9791b5ede355a97f13cf2e6f8249e705d9f85202 | |
parent | 0ddc0e45aec1f71f6c4604ad67cf36cda7181e2b (diff) | |
download | serenity-168cf6fac9c9796884480bd4e98a6a37983c8f3d.zip |
AK: Implement minimum BinaryHeap
This enables efficient implementations of priority queues,
and will also be used in LibCompress for efficient huffman
tree generation.
-rw-r--r-- | AK/BinaryHeap.h | 128 | ||||
-rw-r--r-- | AK/Tests/CMakeLists.txt | 1 | ||||
-rw-r--r-- | AK/Tests/TestBinaryHeap.cpp | 89 |
3 files changed, 218 insertions, 0 deletions
diff --git a/AK/BinaryHeap.h b/AK/BinaryHeap.h new file mode 100644 index 0000000000..0c8be31bd0 --- /dev/null +++ b/AK/BinaryHeap.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2021, Idan Horowitz <idan.horowitz@gmail.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +namespace AK { + +template<typename K, typename V, size_t Capacity> +class BinaryHeap { +public: + BinaryHeap() = default; + ~BinaryHeap() = default; + + // This constructor allows for O(n) construction of the heap (instead of O(nlogn) for repeated insertions) + BinaryHeap(K keys[], V values[], size_t size) + { + VERIFY(size <= Capacity); + m_size = size; + __builtin_memcpy(m_keys, keys, size * sizeof(K)); + __builtin_memcpy(m_values, values, size * sizeof(V)); + + for (ssize_t i = size / 2; i >= 0; i--) { + heapify_down(i); + } + } + + [[nodiscard]] size_t size() const { return m_size; } + [[nodiscard]] bool is_empty() const { return m_size == 0; } + + void insert(K key, V value) + { + VERIFY(m_size < Capacity); + auto index = m_size++; + m_keys[index] = key; + m_values[index] = value; + heapify_up(index); + } + + V pop_min() + { + VERIFY(!is_empty()); + auto index = --m_size; + swap(m_keys[0], m_keys[index]); + swap(m_values[0], m_values[index]); + heapify_down(0); + return m_values[index]; + } + + const V& peek_min() const + { + VERIFY(!is_empty()); + return m_values[0]; + } + + const V& peek_min_key() const + { + VERIFY(!is_empty()); + return m_keys[0]; + } + + void clear() + { + m_size = 0; + } + +private: + void heapify_down(size_t index) + { + while (index * 2 + 1 < m_size) { + auto left_child = index * 2 + 1; + auto right_child = index * 2 + 2; + + auto min_child = left_child; + if (right_child < m_size && m_keys[right_child] < m_keys[min_child]) + min_child = right_child; + + if (m_keys[index] <= m_keys[min_child]) + break; + swap(m_keys[index], m_keys[min_child]); + swap(m_values[index], m_values[min_child]); + index = min_child; + } + } + + void heapify_up(size_t index) + { + while (index != 0) { + auto parent = (index - 1) / 2; + + if (m_keys[index] >= m_keys[parent]) + break; + swap(m_keys[index], m_keys[parent]); + swap(m_values[index], m_values[parent]); + index = parent; + } + } + + K m_keys[Capacity]; + V m_values[Capacity]; + size_t m_size { 0 }; +}; + +} + +using AK::BinaryHeap; diff --git a/AK/Tests/CMakeLists.txt b/AK/Tests/CMakeLists.txt index c7057d5a9d..3d5bc360a2 100644 --- a/AK/Tests/CMakeLists.txt +++ b/AK/Tests/CMakeLists.txt @@ -7,6 +7,7 @@ set(AK_TEST_SOURCES TestAtomic.cpp TestBadge.cpp TestBase64.cpp + TestBinaryHeap.cpp TestBinarySearch.cpp TestBitmap.cpp TestByteBuffer.cpp diff --git a/AK/Tests/TestBinaryHeap.cpp b/AK/Tests/TestBinaryHeap.cpp new file mode 100644 index 0000000000..43d542123a --- /dev/null +++ b/AK/Tests/TestBinaryHeap.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2021, Idan Horowitz <idan.horowitz@gmail.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <AK/TestSuite.h> + +#include <AK/BinaryHeap.h> +#include <AK/String.h> + +TEST_CASE(construct) +{ + BinaryHeap<int, int, 5> empty; + EXPECT(empty.is_empty()); + EXPECT(empty.size() == 0); +} + +TEST_CASE(construct_from_existing) +{ + int keys[] = { 3, 2, 1 }; + char values[] = { 'c', 'b', 'a' }; + BinaryHeap<int, char, 5> from_existing(keys, values, 3); + EXPECT(from_existing.size() == 3); + EXPECT_EQ(from_existing.pop_min(), 'a'); + EXPECT_EQ(from_existing.pop_min(), 'b'); + EXPECT_EQ(from_existing.pop_min(), 'c'); +} + +TEST_CASE(populate_int) +{ + BinaryHeap<int, int, 5> ints; + ints.insert(1, 10); + ints.insert(3, 20); + ints.insert(2, 30); + EXPECT_EQ(ints.size(), 3u); + EXPECT_EQ(ints.pop_min(), 10); + EXPECT_EQ(ints.size(), 2u); + EXPECT_EQ(ints.pop_min(), 30); + EXPECT_EQ(ints.size(), 1u); + EXPECT_EQ(ints.pop_min(), 20); + EXPECT_EQ(ints.size(), 0u); +} + +TEST_CASE(populate_string) +{ + BinaryHeap<int, String, 5> strings; + strings.insert(1, "ABC"); + strings.insert(2, "DEF"); + EXPECT_EQ(strings.size(), 2u); + EXPECT_EQ(strings.pop_min(), "ABC"); + EXPECT_EQ(strings.pop_min(), "DEF"); + EXPECT(strings.is_empty()); +} + +TEST_CASE(large_populate_reverse) +{ + BinaryHeap<int, int, 1024> ints; + for (int i = 1023; i >= 0; i--) { + ints.insert(i, i); + } + EXPECT_EQ(ints.size(), 1024u); + for (int i = 0; i < 1024; i++) { + EXPECT_EQ(ints.peek_min(), i); + EXPECT_EQ(ints.pop_min(), i); + } +} + +TEST_MAIN(BinaryHeap) |