Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ////////////////////////////////////////// main.cpp //////////////////////////////////////////
- #include <iostream>
- #include <cassert>
- #include <string>
- #include "Tree.hpp"
- using KeyType = size_t;
- using ValueType = std::string;
- using TreeType = Tree<KeyType, ValueType>;
- 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<KeyType>{ 3, 5, 6 }));
- assert(tree.balanceFactor() == 0);
- // Проверка удаления корня + проверка балансировки
- tree.insert(7, "");
- tree.erase(5);
- assert(tree.size() == 3U);
- assert((tree.keys() == std::vector<KeyType>{ 3, 6, 7 }));
- assert(tree.balanceFactor() == 0);
- return 0;
- }
- ////////////////////////////////////////// Tree.hpp //////////////////////////////////////////
- #pragma once
- #include <vector>
- #include <functional>
- #include "TreeNode.hpp"
- #include "TreeForwardIterator.hpp"
- #include "TreeReverseIterator.hpp"
- //! Класс, реализующий бинарное дерево поиска
- //!
- //! @tparam KeyT Тип ключа
- //! @tparam ValueT Тип значения
- template <typename KeyT,
- typename ValueT>
- class Tree {
- public:
- using TreeNode = TreeNode<KeyT, ValueT>;
- using ForwardIterator = TreeForwardIterator<TreeNode>;
- using ReverseIterator = TreeReverseIterator<TreeNode>;
- //! Конструктор
- Tree() = default;
- //! Конструктор копирования
- //!
- //! @param other Другой объект дерева
- Tree(const Tree &other) {
- using CopyFunctionT = std::function<TreeNode*(const TreeNode*)>;
- 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<TreeNode**> 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<TreeNode**> 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<KeyT> keys() {
- std::vector<KeyT> 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<TreeNode **> 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 <iterator>
- #include <stack>
- // Article: inorder depth-first traversal
- // http://mike.eshva.ru/dev/obhod-binarnogo-dereva-s-pomoschyu-iteratora
- template <typename TreeNode>
- 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<TreeNode*> _stack;
- TreeNode* _node;
- };
- ////////////////////////////////////////// TreeReverseIterator.hpp //////////////////////////////////////////
- #pragma once
- #include <iterator>
- #include <stack>
- // Article: inorder depth-first traversal
- // http://mike.eshva.ru/dev/obhod-binarnogo-dereva-s-pomoschyu-iteratora
- template <typename TreeNode>
- 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<TreeNode*> _stack;
- TreeNode* _node;
- };
- ////////////////////////////////////////// TreeNode.hpp //////////////////////////////////////////
- #pragma once
- //! Класс, реализующий узел бинарного дерева поиска
- //!
- //! @tparam KeyT Тип ключа
- //! @tparam ValueT Тип значения
- template <typename KeyT,
- typename ValueT>
- 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 <CppUnitTest.h>
- #include "../TemplateTree/Tree.hpp"
- #include <vector>
- #include <string>
- #include <numeric>
- using namespace Microsoft::VisualStudio::CppUnitTestFramework;
- template<> inline
- std::wstring Microsoft::VisualStudio::CppUnitTestFramework::ToString<std::vector<size_t>>(const std::vector<size_t>& 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<KeyType, ValueType>;
- public:
- TEST_METHOD(SimpleTests) {
- TreeType tree;
- Assert::IsTrue(tree.size() == 0);
- Assert::IsTrue(tree.balanceFactor() == 0);
- Assert::IsTrue(tree.keys() == std::vector<KeyType>{});
- // Элемент добавился в корень дерева
- tree.insert(5, "пять");
- Assert::IsTrue(tree.size() == 1U);
- Assert::IsTrue(tree.balanceFactor() == 0);
- Assert::IsTrue(tree.keys() == std::vector<KeyType>{ 5 });
- // Элемент добавился в левый лист
- tree.insert(3, "три");
- Assert::IsTrue(tree.size() == 2U);
- Assert::IsTrue(tree.balanceFactor() == 1);
- Assert::IsTrue(tree.keys() == std::vector<KeyType>{ 3, 5 });
- // Элемент добавился в правый лист
- tree.insert(6, "шесть");
- Assert::IsTrue(tree.size() == 3U);
- Assert::IsTrue(tree.balanceFactor() == 0);
- Assert::IsTrue(tree.keys() == std::vector<KeyType>{ 3, 5, 6 });
- tree.insert(7, "семь");
- Assert::IsTrue(tree.size() == 4U);
- Assert::IsTrue(tree.balanceFactor() == -1);
- Assert::IsTrue(tree.keys() == std::vector<KeyType>{ 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<KeyType>{ 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<KeyType>{ 3, 6, 7 });
- }
- };
Advertisement