From 39aff3bf70603c988588fe8c16f9b665657bd8d2 Mon Sep 17 00:00:00 2001 From: Sergey Esipenko Date: Mon, 7 Nov 2022 21:16:48 +0300 Subject: [PATCH 1/2] add homework 7 --- .../ads/bst/RedBlackBinarySearchTree.java | 89 +++++ .../ads/bst/AvlBinarySearchTreeTest.java | 2 + .../ads/bst/RedBlackBinarySearchTreeTest.java | 362 ++++++++++++++++++ 3 files changed, 453 insertions(+) create mode 100644 src/main/java/company/vk/polis/ads/bst/RedBlackBinarySearchTree.java create mode 100644 src/test/java/company/vk/polis/ads/bst/RedBlackBinarySearchTreeTest.java diff --git a/src/main/java/company/vk/polis/ads/bst/RedBlackBinarySearchTree.java b/src/main/java/company/vk/polis/ads/bst/RedBlackBinarySearchTree.java new file mode 100644 index 00000000..c583fa2a --- /dev/null +++ b/src/main/java/company/vk/polis/ads/bst/RedBlackBinarySearchTree.java @@ -0,0 +1,89 @@ +package company.vk.polis.ads.bst; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * LLRB implementation of binary search tree. + */ +public class RedBlackBinarySearchTree, Value> + implements BinarySearchTree { + + private static final boolean BLACK = false; + private static final boolean RED = true; + + private class Node { + Key key; + Value value; + Node left; + Node right; + boolean color; + } + + @Nullable + @Override + public Value get(@NotNull Key key) { + throw new UnsupportedOperationException("Implement me"); + } + + @Override + public void put(@NotNull Key key, @NotNull Value value) { + throw new UnsupportedOperationException("Implement me"); + } + + @Nullable + @Override + public Value remove(@NotNull Key key) { + throw new UnsupportedOperationException("Implement me"); + } + + @Nullable + @Override + public Key min() { + throw new UnsupportedOperationException("Implement me"); + } + + @Nullable + @Override + public Value minValue() { + throw new UnsupportedOperationException("Implement me"); + } + + @Nullable + @Override + public Key max() { + throw new UnsupportedOperationException("Implement me"); + } + + @Nullable + @Override + public Value maxValue() { + throw new UnsupportedOperationException("Implement me"); + } + + @Nullable + @Override + public Key floor(@NotNull Key key) { + throw new UnsupportedOperationException("Implement me"); + } + + @Nullable + @Override + public Key ceil(@NotNull Key key) { + throw new UnsupportedOperationException("Implement me"); + } + + @Override + public int size() { + throw new UnsupportedOperationException("Implement me"); + } + + /** + * Только для тестов + * Саму высоту хранить не обязательно, достаточно сделать рекурсивное вычисление + */ + @Override + public int height() { + throw new UnsupportedOperationException("Implement me"); + } +} diff --git a/src/test/java/company/vk/polis/ads/bst/AvlBinarySearchTreeTest.java b/src/test/java/company/vk/polis/ads/bst/AvlBinarySearchTreeTest.java index 04f41113..1bcfa503 100644 --- a/src/test/java/company/vk/polis/ads/bst/AvlBinarySearchTreeTest.java +++ b/src/test/java/company/vk/polis/ads/bst/AvlBinarySearchTreeTest.java @@ -1,5 +1,6 @@ package company.vk.polis.ads.bst; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -10,6 +11,7 @@ /** * Basic binary search tree invariants. */ +@Disabled("Disabled since 7th homework released") class AvlBinarySearchTreeTest { BinarySearchTree newAvlBst() { diff --git a/src/test/java/company/vk/polis/ads/bst/RedBlackBinarySearchTreeTest.java b/src/test/java/company/vk/polis/ads/bst/RedBlackBinarySearchTreeTest.java new file mode 100644 index 00000000..02416353 --- /dev/null +++ b/src/test/java/company/vk/polis/ads/bst/RedBlackBinarySearchTreeTest.java @@ -0,0 +1,362 @@ +package company.vk.polis.ads.bst; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Basic binary search tree invariants. + */ +class RedBlackBinarySearchTreeTest { + + BinarySearchTree newRedBlackBst() { + return new RedBlackBinarySearchTree<>(); + } + + @Test + void emptyBst() { + BinarySearchTree bst = newRedBlackBst(); + assertNull(bst.get("")); + assertNull(bst.get("some key")); + assertEquals(0, bst.size()); + assertEquals(0, bst.height()); + } + + @Test + void orderedOnEmpty() { + BinarySearchTree bst = newRedBlackBst(); + assertNull(bst.ceil("some key")); + assertNull(bst.floor("some key")); + + assertNull(bst.min()); + assertNull(bst.max()); + + assertNull(bst.minValue()); + assertNull(bst.maxValue()); + } + + @Test + void put() { + BinarySearchTree bst = newRedBlackBst(); + bst.put("foo", "bar"); + + assertEquals("bar", bst.get("foo")); + + assertEquals(1, bst.size()); + assertEquals(1, bst.height()); + } + + @Test + void replace() { + BinarySearchTree bst = newRedBlackBst(); + bst.put("foo", "bar"); + bst.put("foo", "bee"); + + assertEquals("bee", bst.get("foo")); + + assertEquals(1, bst.size()); + assertEquals(1, bst.height()); + } + + @Test + void morePut() { + BinarySearchTree bst = newRedBlackBst(); + + int size = 0; + assertEquals(bst.size(), size); + assertNull(bst.max()); + assertNull(bst.min()); + assertNull(bst.get("testStringKey1")); + + bst.put("testStringKey1", "testStringValue1"); + + assertEquals(bst.size(), ++size); + assertEquals(bst.min(), "testStringKey1"); + assertEquals(bst.max(), "testStringKey1"); + assertEquals(bst.get("testStringKey1"), "testStringValue1"); + + bst.put("testStringKey2", "testStringValue2"); + + assertEquals(bst.size(), ++size); + assertEquals(bst.min(), "testStringKey1"); + assertEquals(bst.max(), "testStringKey2"); + assertEquals(bst.maxValue(), "testStringValue2"); + assertEquals(bst.get("testStringKey2"), "testStringValue2"); + + bst.put("testStringKey2", "case with same value"); + + assertEquals(bst.size(), size); + assertEquals(bst.min(), "testStringKey1"); + assertEquals(bst.max(), "testStringKey2"); + assertEquals(bst.maxValue(), "case with same value"); + assertEquals(bst.get("testStringKey2"), "case with same value"); + + bst.put("testStringKey3", "testStringValue3"); + + assertEquals(bst.size(), ++size); + assertEquals(bst.min(), "testStringKey1"); + assertEquals(bst.max(), "testStringKey3"); + assertEquals(bst.get("testStringKey3"), "testStringValue3"); + + bst.put("testStringKey", "testStringValue"); + + assertEquals(bst.size(), ++size); + assertEquals(bst.min(), "testStringKey"); + assertEquals(bst.max(), "testStringKey3"); + assertEquals(bst.get("testStringKey"), "testStringValue"); + } + + @Test + void remove() { + BinarySearchTree bst = newRedBlackBst(); + assertNull(bst.remove("case when bst is empty")); + assertTrue(bst.isEmpty()); + + bst.put("testStringKey3", "testStringValue3"); + bst.put("testStringKey4", "testStringValue4"); + bst.put("testStringKey2", "testStringValue2"); + bst.put("testStringKey5", "testStringValue5"); + bst.put("testStringKey1", "testStringValue1"); + bst.put("testStringKey0", "testStringValue0"); + + assertFalse(bst.isEmpty()); + int size = bst.size(); + + assertEquals(bst.remove("testStringKey4"), "testStringValue4"); + assertEquals(bst.size(), --size); + assertFalse(bst.containsKey("testStringKey4")); + + assertEquals(bst.remove("testStringKey1"), "testStringValue1"); + assertEquals(bst.size(), --size); + assertFalse(bst.containsKey("testStringKey1")); + + assertNull(bst.remove("testStringKey1"), "testStringValue1"); + assertEquals(bst.size(), size); + assertFalse(bst.isEmpty()); + assertFalse(bst.containsKey("testStringKey1")); + + assertEquals(bst.remove("testStringKey3"), "testStringValue3"); + assertEquals(bst.size(), --size); + assertFalse(bst.containsKey("testStringKey3")); + + assertEquals(bst.remove("testStringKey0"), "testStringValue0"); + assertEquals(bst.size(), --size); + assertFalse(bst.containsKey("testStringKey0")); + + assertEquals(bst.remove("testStringKey2"), "testStringValue2"); + assertEquals(bst.size(), --size); + assertFalse(bst.containsKey("testStringKey2")); + + assertEquals(bst.remove("testStringKey5"), "testStringValue5"); + assertEquals(bst.size(), --size); + assertFalse(bst.containsKey("testStringKey5")); + + assertTrue(bst.isEmpty()); + } + + @Test + void max() { + BinarySearchTree bst = newRedBlackBst(); + + assertNull(bst.max()); + assertNull(bst.maxValue()); + + bst.put("testStringKey2", "testStringValue2"); + + assertEquals(bst.max(), "testStringKey2"); + assertEquals(bst.maxValue(), "testStringValue2"); + + bst.put("testStringKey5", "testStringValue5"); + + assertEquals(bst.max(), "testStringKey5"); + assertEquals(bst.maxValue(), "testStringValue5"); + + bst.put("testStringKey0", "testStringValue0"); + + assertEquals(bst.max(), "testStringKey5"); + assertEquals(bst.maxValue(), "testStringValue5"); + + bst.put("testStringKey7", "testStringValue7"); + + assertEquals(bst.max(), "testStringKey7"); + assertEquals(bst.maxValue(), "testStringValue7"); + + bst.remove("testStringKey5"); + + assertEquals(bst.max(), "testStringKey7"); + assertEquals(bst.maxValue(), "testStringValue7"); + + bst.remove("testStringKey7"); + + assertEquals(bst.max(), "testStringKey2"); + assertEquals(bst.maxValue(), "testStringValue2"); + + bst.remove("testStringKey0"); + + assertEquals(bst.max(), "testStringKey2"); + assertEquals(bst.maxValue(), "testStringValue2"); + + bst.remove("testStringKey2"); + + assertNull(bst.max()); + assertNull(bst.maxValue()); + } + + @Test + void min() { + BinarySearchTree bst = newRedBlackBst(); + + assertNull(bst.min()); + assertNull(bst.minValue()); + + bst.put("testStringKey5", "testStringValue5"); + + assertEquals(bst.min(), "testStringKey5"); + assertEquals(bst.minValue(), "testStringValue5"); + + bst.put("testStringKey3", "testStringValue3"); + + assertEquals(bst.min(), "testStringKey3"); + assertEquals(bst.minValue(), "testStringValue3"); + + bst.put("testStringKey9", "testStringValue9"); + + assertEquals(bst.min(), "testStringKey3"); + assertEquals(bst.minValue(), "testStringValue3"); + + bst.put("testStringKey0", "testStringValue0"); + + assertEquals(bst.min(), "testStringKey0"); + assertEquals(bst.minValue(), "testStringValue0"); + + bst.remove("testStringKey5"); + + assertEquals(bst.min(), "testStringKey0"); + assertEquals(bst.minValue(), "testStringValue0"); + + bst.remove("testStringKey0"); + + assertEquals(bst.min(), "testStringKey3"); + assertEquals(bst.minValue(), "testStringValue3"); + + bst.remove("testStringKey9"); + + assertEquals(bst.min(), "testStringKey3"); + assertEquals(bst.minValue(), "testStringValue3"); + + bst.remove("testStringKey3"); + + assertNull(bst.min()); + assertNull(bst.minValue()); + } + + @Test + void contains() { + BinarySearchTree bst = newRedBlackBst(); + + assertFalse(bst.containsKey("testStringKey")); + assertFalse(bst.containsKey("testStringKey1")); + + bst.put("testStringKey", "testStringValue"); + assertTrue(bst.containsKey("testStringKey")); + assertFalse(bst.containsKey("testStringKey1")); + + bst.put("testStringKey1", "testStringValue1"); + assertTrue(bst.containsKey("testStringKey1")); + assertTrue(bst.containsKey("testStringKey")); + + bst.remove("testStringKey"); + assertTrue(bst.containsKey("testStringKey1")); + assertFalse(bst.containsKey("testStringKey")); + + bst.remove("testStringKey1"); + assertFalse(bst.containsKey("testStringKey")); + assertFalse(bst.containsKey("testStringKey1")); + } + + @Test + void empty() { + BinarySearchTree bst = newRedBlackBst(); + + assertTrue(bst.isEmpty()); + + bst.put("testStringKey", "testStringValue"); + assertFalse(bst.isEmpty()); + + bst.put("testStringKey1", "testStringValue1"); + assertFalse(bst.isEmpty()); + + bst.remove("testStringKey"); + assertFalse(bst.isEmpty()); + + bst.remove("testStringKey1"); + assertTrue(bst.isEmpty()); + } + + @Test + void ceil() { + BinarySearchTree bst = newRedBlackBst(); + + bst.put("1", "testStringValue3"); + bst.put("3", "testStringValue4"); + bst.put("5", "testStringValue2"); + bst.put("7", "testStringValue5"); + bst.put("8", "testStringValue1"); + bst.put("9", "testStringValue0"); + bst.put("2", "testStringValue0"); + + assertEquals(bst.min(), "1"); + assertEquals(bst.max(), "9"); + assertEquals(bst.ceil("5"), "5"); + assertEquals(bst.ceil("2"), "2"); + assertEquals(bst.ceil("8"), "8"); + assertEquals(bst.ceil("0"), "1"); + assertEquals(bst.ceil("9"), "9"); + assertNull(bst.ceil("99")); + } + + @Test + void floor() { + BinarySearchTree bst = newRedBlackBst(); + + bst.put("1", "testStringValue3"); + bst.put("3", "testStringValue4"); + bst.put("5", "testStringValue2"); + bst.put("7", "testStringValue5"); + bst.put("8", "testStringValue1"); + bst.put("9", "testStringValue0"); + bst.put("2", "testStringValue0"); + + assertEquals(bst.min(), "1"); + assertEquals(bst.max(), "9"); + assertEquals(bst.floor("5"), "5"); + assertEquals(bst.floor("4"), "3"); + assertEquals(bst.floor("8"), "8"); + assertEquals(bst.floor("1"), "1"); + assertEquals(bst.floor("99"), "9"); + assertNull(bst.floor("")); + } + + @Test + void moreReplace() { + BinarySearchTree bst = newRedBlackBst(); + + assertNull(bst.get("1")); + + bst.put("1", "testStringValue3"); + assertEquals(bst.get("1"), "testStringValue3"); + + bst.put("1", "testStringValue4"); + assertEquals(bst.get("1"), "testStringValue4"); + + bst.put("1", "testStringValue2"); + assertEquals(bst.get("1"), "testStringValue2"); + + bst.put("7", "testStringValue5"); + assertEquals(bst.get("7"), "testStringValue5"); + assertEquals(bst.get("1"), "testStringValue2"); + } +} \ No newline at end of file From 25dfbc7749f18398b6d150de4e026d7ddcae5a71 Mon Sep 17 00:00:00 2001 From: Sergey Esipenko Date: Tue, 8 Nov 2022 20:58:51 +0300 Subject: [PATCH 2/2] update readme --- README.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/README.md b/README.md index fe7aaa78..a8c4efc7 100644 --- a/README.md +++ b/README.md @@ -152,3 +152,39 @@ $ git checkout -b fe2f22405db89b95bf9b5a034f95daf4c61e29ab ```commandline ./gradlew test ``` + +## ДЗ 7. RB-tree. Дедлайн 15.11.2022 18:29:59 + +‼️ Ветка с решением должна ответвляться от коммита [TODO](https://github.com/polis-vk/2022-ads/commit/TODO): + +```bash +$ git fetch upstream +$ git checkout -b TODO +``` + +Реализовать Left-Leaning Red-Black Tree в `company.vk.polis.ads.bst.RedBlackBinarySearchTree`, чтобы выполнялись тесты `company.vk.polis.ads.bst.RedBlackBinarySearchTreeTest`. + +Решения с использованием библиотечных структур данных +(`TreeMap`, которая на самом деле работает на красно-черных деревьях из Кормена) не принимаются. + +Основные методы: + * `Value get(Key key)` - возвращает значение по заданному ключу или `null`, если такого ключа нет + * `void put(Key key, Value value)` - сохраняет заданное значение по указанному ключу + * `Value remove(Key key)` - удаляет заданный ключ (и связанное с ним значение, возвращая его) + +Методы, основанные на порядке ключей: + * `Key min()` - возвращает минимальный ключ или `null`, если структура пустая + * `Value minValue()` - возвращает значение, ассоцирированное с минимальным ключом, или `null`, если структура пустая + * `Key max()` - возвращает максимальный ключ или `null`, если структура пустая + * `Value maxValue()` - возвращает значение, ассоцирированное с максимальным ключом, или `null`, если структура пустая + * `Key floor(Key key)` - возвращает максимальный ключ, меньший либо равный заданному, или `null`, если такого нет + * `Key ceil(Key key)` - вовзращает минимальный ключ, больший либо равный заданному, или `null`, если такого нет + +Служебные методы: + * `int size()` - вовзращает количество узлов в дереве + * `int height()` - возвращает высоту дерева (достаточно простой рекурсивной реализации, хранить высоту в узле не обязательно) + +Локально запускать тесты можно через +```commandline +./gradlew test +```