////////////////////////////////////////// main.cpp ////////////////////////////////////////// #include #include #include #include "Tree.hpp" using KeyType = size_t; using ValueType = std::string; using TreeType = Tree; int main() { TreeType tree; tree.insert(5, ""); tree.insert(3, ""); tree.insert(6, ""); tree.insert(7, ""); // Проверка удаления листа (не корня) tree.erase(7); assert(tree.size() == 3U); assert((tree.keys() == std::vector{ 3, 5, 6 })); assert(tree.balanceFactor() == 0); // Проверка удаления корня + проверка балансировки tree.insert(7, ""); tree.erase(5); assert(tree.size() == 3U); assert((tree.keys() == std::vector{ 3, 6, 7 })); assert(tree.balanceFactor() == 0); return 0; } ////////////////////////////////////////// Tree.hpp ////////////////////////////////////////// #pragma once #include #include #include "TreeNode.hpp" #include "TreeForwardIterator.hpp" #include "TreeReverseIterator.hpp" //! Класс, реализующий бинарное дерево поиска //! //! @tparam KeyT Тип ключа //! @tparam ValueT Тип значения template class Tree { public: using TreeNode = TreeNode; using ForwardIterator = TreeForwardIterator; using ReverseIterator = TreeReverseIterator; //! Конструктор Tree() = default; //! Конструктор копирования //! //! @param other Другой объект дерева Tree(const Tree &other) { using CopyFunctionT = std::function; const CopyFunctionT copy = [&](const TreeNode* source) { TreeNode* dist = new TreeNode(source->key, source->value); if (source->left) { dist->left = copy(source->left); } if (source->right) { dist->right = copy(source->right); } return dist; }; _root = copy(other._root); } //! Деструктор ~Tree() { clearNode(_root); } //! Размер дерева //! //! @return Количество нод в дереве std::size_t size() const { return _size; } //! Отчистка дерева void clear() { clearNode(_root); _root = nullptr; } //! Проверка дерева на пустоту bool empty() const { return _size == 0; } //! Доступ к данным по ключу. //! //! @param key Ключ элемента в дереве //! @return Значение элемента внутри дерева ValueT& operator()(const KeyT& key) const { TreeNode* node = _find(key, _root); return node->value; } //! Добавление элемента в дерево //! //! @param key Ключ //! @param value Значение void insert(const KeyT& key, const ValueT& value) { TreeNode** indirect = &_root; // Чтобы обобщить вставку std::vector path; // while (*indirect != nullptr) { path.push_back(indirect); if ((*indirect)->key > key) indirect = &((*indirect)->left); else indirect = &((*indirect)->right); } *indirect = new TreeNode(key, value); path.push_back(indirect); _balance(path); _size++; } //! Удаление элемента из дерева //! //! @param key Ключ void erase(const KeyT& key) { TreeNode** indirect = &_root; // to generalize insertion std::vector path; // to update height values while (*indirect != nullptr && (*indirect)->key != key) { path.push_back(indirect); if ((*indirect)->key > key) indirect = &((*indirect)->left); else indirect = &((*indirect)->right); } // В дереве нет ноды с таким ключем if (*indirect == nullptr) { throw std::runtime_error("Key doesn't exist"); } else { path.push_back(indirect); } std::size_t index = path.size(); // Удаляемая нода является листом - нет поддеревьев if ((*indirect)->left == nullptr || (*indirect)->right == nullptr) { delete *indirect; // Просто удаляем ноду *indirect = nullptr; path.pop_back(); } else if ((*indirect)->right == nullptr) { // Существует только левое поддерево TreeNode *remove = *indirect; (*indirect) = (*indirect)->left; delete remove; path.pop_back(); } else { // Существует только правое поддерево TreeNode **successor = &((*indirect)->right); while ((*successor)->left != nullptr) { path.push_back(successor); successor = &((*successor)->left); } if (*successor == (*indirect)->right) { (*successor)->left = (*indirect)->left; TreeNode *toRemove = *indirect; *indirect = *successor; delete toRemove; } else { TreeNode *tmp = *path.back(); TreeNode *suc = *successor; tmp->left = (*successor)->right; suc->left = (*indirect)->left; suc->right = (*indirect)->right; delete *indirect; *indirect = suc; path[index] = &(suc->right); } } _balance(path); _size--; } //! Список ключей в дереве std::vector keys() { std::vector result; for (auto it = begin(); it != end(); ++it) { result.push_back(it->key); } return result; } //! Доп. операция в варианте задания - определение критерия сбалансированности int balanceFactor() const { if (!_root) return 0; return _root->balanceFactor(); } //! Получаем итератор на первый элемент в дереве //! //! @return Итератор, указывающий на первый элемент в дереве ForwardIterator begin() { return ForwardIterator(_root); } //! Получаем обратный итератор на последний элемент в дереве //! //! @return Обратный итератор, указывающий на последний элемент в дереве ReverseIterator rbegin() { return ReverseIterator(_root); } //! Получаем итератор, указывающий на элемент, после последнего //! //! @return Итератор, указывающий на элемент после последнего ForwardIterator end() { return ForwardIterator(nullptr); } //! Получаем обратный итератор, указывающий на элемент, перед первым //! //! @return Обратный итератор, указывающий на элемент, перед первым ReverseIterator rend() { return ReverseIterator(nullptr); } private: //! Рекурсивный поиск ноды по ключу TreeNode* _find(const KeyT& key, TreeNode* node) const { if (!node) { return nullptr; } if (key == node->key) { return node; } if (key < node->key) { return _find(key, node->left); } else { return _find(key, node->right); } } //! Процедура балансировки дерева //! https://github.com/KadirEmreOto/AVL-Tree void _balance(std::vector path) { // Начинаем балансировать с корня к листу std::reverse(path.begin(), path.end()); for (auto indirect : path) { TreeNode* node = *indirect; TreeNode* leftLeaf = node->left; TreeNode* rightLeaf = node->right; node->updateValues(); if (node->balanceFactor() >= 2 || (leftLeaf && leftLeaf->balanceFactor() > 0)) { // left - left *indirect = (*indirect)->rotateR(); } else if (node->balanceFactor() >= 2) { // left - right (*indirect)->left = (*indirect)->left->rotateL(); *indirect = (*indirect)->rotateR(); } else if (node->balanceFactor() <= -2 || (rightLeaf && rightLeaf->balanceFactor() < 0)) { // right - right } else if (node->balanceFactor() <= -2 || (rightLeaf && rightLeaf->balanceFactor() < 0)) { // right - right *indirect = (*indirect)->rotateL(); } else if (node->balanceFactor() <= -2) { // right - left (*indirect)->right = ((*indirect)->right)->rotateR(); *indirect = (*indirect)->rotateL(); } } } //! Удаляем поддерево void clearNode(TreeNode* node) { if (node->left) clearNode(node->left); if (node->right) clearNode(node->right); delete node; }; private: TreeNode* _root = nullptr; size_t _size = 0; }; ////////////////////////////////////////// TreeForwardIterator.hpp ////////////////////////////////////////// #pragma once #include #include // Article: inorder depth-first traversal // http://mike.eshva.ru/dev/obhod-binarnogo-dereva-s-pomoschyu-iteratora template class TreeForwardIterator { public: explicit TreeForwardIterator(TreeNode* root) : _node(root) { if (!_node) return; _stack.push(nullptr); while (_node->left) { _stack.push(_node); _node = _node->left; } }; bool operator==(const TreeForwardIterator& other) const { // Равны, если оба итератора указывают на nullptr if (!_node && !other._node) return true; // Не равны, если один из них nullptr if ((!_node && other._node) || (_node && !other._node)) return false; // Равны, если указывают на одинаковый ключ (и значение?) if (_node->key == other._node->key) return true; return false; } bool operator!=(const TreeForwardIterator& other) const { return !operator==(other); } TreeNode* operator->() { if (!_node) { throw std::runtime_error("Iterator not dereferenceable"); } return _node; } TreeForwardIterator& operator++() { if (!_node) return *this; if (_node->right) { _node = _node->right; while (_node->left) { _stack.push(_node); _node = _node->left; } } else { _node = _stack.top(); _stack.pop(); } return *this; } private: std::stack _stack; TreeNode* _node; }; ////////////////////////////////////////// TreeReverseIterator.hpp ////////////////////////////////////////// #pragma once #include #include // Article: inorder depth-first traversal // http://mike.eshva.ru/dev/obhod-binarnogo-dereva-s-pomoschyu-iteratora template class TreeReverseIterator { public: explicit TreeReverseIterator(TreeNode* root) : _node(root) { if (!_node) return; _stack.push(nullptr); while (_node->right) { _stack.push(_node); _node = _node->right; } }; bool operator==(const TreeReverseIterator& other) const { // Равны, если оба итератора указывают на nullptr if (!_node && !other._node) return true; // Не равны, если один из них nullptr if ((!_node && other._node) || (_node && !other._node)) return false; // Равны, если указывают на одинаковый ключ (и значение?) if (_node->key == other._node->key) return true; return false; } bool operator!=(const TreeReverseIterator& other) const { return !operator==(other); } TreeNode* operator->() { if (!_node) { throw std::runtime_error("Iterator not dereferenceable"); } return _node; } TreeReverseIterator& operator++() { if (!_node) return *this; if (_node->left) { _node = _node->left; while (_node->right) { _stack.push(_node); _node = _node->right; } } else { _node = _stack.top(); _stack.pop(); } return *this; } private: std::stack _stack; TreeNode* _node; }; ////////////////////////////////////////// TreeNode.hpp ////////////////////////////////////////// #pragma once //! Класс, реализующий узел бинарного дерева поиска //! //! @tparam KeyT Тип ключа //! @tparam ValueT Тип значения template struct TreeNode { // Данные, хранящиеся в элементе KeyT key; ValueT value; // Высота элемента size_t height = 1; // Количество элементов в поддереве size_t count = 1; // Указатель на дочерние элементы TreeNode* left = nullptr; TreeNode* right = nullptr; void updateValues() { const auto leftCount = (left != nullptr) ? left->count : 0; const auto rightCount = (right != nullptr) ? right->count : 0; count = leftCount + rightCount + 1; const auto leftHeight = (left != nullptr) ? left->height : 0; const auto rightHeight = (right != nullptr) ? right->height : 0; height = leftHeight + rightHeight + 1; } int balanceFactor() const { const auto leftHeight = (left != nullptr) ? left->height : 0; const auto rightHeight = (right != nullptr) ? right->height : 0; return leftHeight - rightHeight; } // Поворот дерева вокруг узла TreeNode* rotateL() { TreeNode* root = right; // Правый лист стал корнем right = right->left; root->left = this; this->updateValues(); // Порядок обновления важен root->updateValues(); return root; } TreeNode* rotateR() { TreeNode* root = left; // Левый лист стал корнем left = left->right; root->right = this; this->updateValues(); // Порядок обновления важен root->updateValues(); return root; } TreeNode(const KeyT& key, const ValueT& value) : key(key), value(value) {} }; ////////////////////////////////////////// TemplateTreeTest.cpp ////////////////////////////////////////// #include #include "../TemplateTree/Tree.hpp" #include #include #include using namespace Microsoft::VisualStudio::CppUnitTestFramework; template<> inline std::wstring Microsoft::VisualStudio::CppUnitTestFramework::ToString>(const std::vector& vector) { return std::accumulate(std::begin(vector), std::end(vector), std::wstring{}, [](std::wstring &ss, const unsigned int &s) { return ss.empty() ? std::to_wstring(s) : ss + L"," + std::to_wstring(s); }); } TEST_CLASS(TemplateTreeTest) { private: using KeyType = size_t; using ValueType = std::string; using TreeType = Tree; public: TEST_METHOD(SimpleTests) { TreeType tree; Assert::IsTrue(tree.size() == 0); Assert::IsTrue(tree.balanceFactor() == 0); Assert::IsTrue(tree.keys() == std::vector{}); // Элемент добавился в корень дерева tree.insert(5, "пять"); Assert::IsTrue(tree.size() == 1U); Assert::IsTrue(tree.balanceFactor() == 0); Assert::IsTrue(tree.keys() == std::vector{ 5 }); // Элемент добавился в левый лист tree.insert(3, "три"); Assert::IsTrue(tree.size() == 2U); Assert::IsTrue(tree.balanceFactor() == 1); Assert::IsTrue(tree.keys() == std::vector{ 3, 5 }); // Элемент добавился в правый лист tree.insert(6, "шесть"); Assert::IsTrue(tree.size() == 3U); Assert::IsTrue(tree.balanceFactor() == 0); Assert::IsTrue(tree.keys() == std::vector{ 3, 5, 6 }); tree.insert(7, "семь"); Assert::IsTrue(tree.size() == 4U); Assert::IsTrue(tree.balanceFactor() == -1); Assert::IsTrue(tree.keys() == std::vector{ 3, 5, 6, 7 }); } TEST_METHOD(ReverseIteratorTest) { TreeType tree; tree.insert(5, ""); tree.insert(3, ""); tree.insert(6, ""); tree.insert(7, ""); // Проверка обратного итератора auto it = tree.rbegin(); Assert::IsTrue(it->key == 7U); ++it; Assert::IsTrue(it->key == 6U); ++it; Assert::IsTrue(it->key == 5U); ++it; Assert::IsTrue(it->key == 3U); ++it; Assert::IsTrue(it == tree.rend()); } TEST_METHOD(RemoveTest) { TreeType tree; tree.insert(5, ""); tree.insert(3, ""); tree.insert(6, ""); tree.insert(7, ""); // Проверка удаления не вершины tree.erase(7); Assert::IsTrue(tree.size() == 3U); Assert::IsTrue(tree.balanceFactor() == 0); Assert::IsTrue(tree.keys() == std::vector{ 3, 5, 6 }); // Проверка удаления вершины + проверка балансировки tree.insert(7, ""); tree.erase(5); Assert::IsTrue(tree.size() == 3U); Assert::IsTrue(tree.balanceFactor() == 0); Assert::IsTrue(tree.keys() == std::vector{ 3, 6, 7 }); } };