#include <iostream>
#include <string>
#include <memory>
#include <functional>
template <typename T, typename Comparator>
class BinaryTree {
public:
enum TreeTraversal {Preorder, Postorder, Inorder};
private:
class Node : public std::enable_shared_from_this<Node> {
private:
BinaryTree& binaryTree; // Reference data member because every BinaryTree::Node belongs to a BinaryTree.
T value;
std::shared_ptr<Node> left = nullptr, right = nullptr, parent = nullptr;
std::size_t subtreeSize, subtreeHeight, depthFromRoot;
friend class BinaryTree;
public:
Node (BinaryTree& tree, const T& t, std::size_t size = 1, std::size_t height = 0, std::size_t depth = 0) : binaryTree(tree), value(t), subtreeSize(size), subtreeHeight(height), depthFromRoot(depth) {}
Comparator getComparator() const {return binaryTree.comparator;}
private:
Node (const Node&);
Node (Node&&);
Node& operator= (const Node&);
Node& operator= (Node&&);
inline std::shared_ptr<Node> insert (BinaryTree&, const T&, std::shared_ptr<Node>&);
inline std::shared_ptr<Node> search (const T&, std::shared_ptr<Node>&);
inline std::shared_ptr<Node> remove (const T&, std::shared_ptr<Node>&);
inline std::shared_ptr<Node> findMaxNode (const std::shared_ptr<Node>&) const;
inline std::shared_ptr<Node> removeMaxNode (std::shared_ptr<Node>&, std::shared_ptr<Node>&);
bool isRoot() const {return parent == nullptr;}
inline void setLeft (std::shared_ptr<Node>&);
inline void setRight (std::shared_ptr<Node>&);
inline void setLeftMinus (std::shared_ptr<Node>&);
inline void setRightMinus (std::shared_ptr<Node>&);
void setLeftSimple (std::shared_ptr<Node>& node) {left = node; if (node) node->parent = this->shared_from_this();}
void setRightSimple (std::shared_ptr<Node>& node) {right = node; if (node) node->parent = this->shared_from_this();}
};
template <TreeTraversal, typename = void> struct ExecuteActionHelper;
private:
std::shared_ptr<Node> root;
const Comparator comparator;
public:
BinaryTree (const Comparator& comp = Comparator()) : root(nullptr), comparator(comp) {}
BinaryTree (const BinaryTree<T, Comparator>& other) : root(std::shared_ptr<Node>(new Node(*other.root))), comparator(other.comparator) {} // 'root(std::shared_ptr<Node>(other.root))' would also copy the entire tree, but it would be a shallow copy (no new nodes are being instantiated).
BinaryTree (BinaryTree<T, Comparator>&& other) : root(other.root), comparator(other.comparator) {other.root = nullptr;}
BinaryTree& operator= (const BinaryTree&);
BinaryTree& operator= (BinaryTree&&);
std::shared_ptr<Node> insert (const T& t) {return root->insert(*this, t, root);}
std::shared_ptr<Node> remove (const T& t) {return root->remove(t, root);}
std::shared_ptr<Node> search (const T& t) {std::shared_ptr<Node> node = root->search (t, root); std::cout << node->value << " found.\n"; return node;}
template <TreeTraversal Tr = Preorder, typename F = std::function<void(T)>> void executeAction (const F& f, std::shared_ptr<Node> startingNode) const {executeActionHelper<Tr> (startingNode, f);}
template <TreeTraversal Tr = Preorder, typename F = std::function<void(T)>> void executeAction (const F& f) const {executeAction<Tr> (f, root);}
inline void display() const;
void replaceRoot (std::shared_ptr<Node> node) {root = node; root->parent = nullptr;}
void rotateLeft (std::shared_ptr<Node>&);
void rotateRight (std::shared_ptr<Node>&);
inline void decreaseDepthFromRoot (std::shared_ptr<Node>& node) {adjustDepthFromRoot(node, -1);}
inline void increaseDepthFromRoot (std::shared_ptr<Node>& node) {adjustDepthFromRoot(node, 1);}
private:
inline void displayNodeAndChildren (const std::shared_ptr<Node>&) const;
template <TreeTraversal Tr, typename F> void executeActionHelper (const std::shared_ptr<Node>& node, const F& f) const {ExecuteActionHelper<Tr>::template execute(node, f);}
inline void adjustDepthFromRoot (std::shared_ptr<Node>&, int);
};
template <typename T, typename Comparator>
template <typename P>
struct BinaryTree<T, Comparator>::ExecuteActionHelper<BinaryTree<T, Comparator>::Preorder, P> {
template <typename F>
static void execute (const std::shared_ptr<BinaryTree<T, Comparator>::Node>& node, const F& f) {
if (!node)
return;
f(node->value);
execute (node->left, f);
execute (node->right, f);
}
};
template <typename T, typename Comparator>
template <typename P>
struct BinaryTree<T, Comparator>::ExecuteActionHelper<BinaryTree<T, Comparator>::Postorder, P> {
template <typename F>
static void execute (const std::shared_ptr<BinaryTree<T, Comparator>::Node>& node, const F& f) {
if (!node)
return;
execute (node->left, f);
execute (node->right, f);
f(node->value);
}
};
template <typename T, typename Comparator>
template <typename P>
struct BinaryTree<T, Comparator>::ExecuteActionHelper<BinaryTree<T, Comparator>::Inorder, P> {
template <typename F>
static void execute (const std::shared_ptr<BinaryTree<T, Comparator>::Node>& node, const F& f) {
if (!node)
return;
execute (node->left, f);
f(node->value);
execute (node->right, f);
}
};
template <typename T, typename Comparator>
inline std::shared_ptr<typename BinaryTree<T, Comparator>::Node>
BinaryTree<T, Comparator>::Node::insert (BinaryTree& tree, const T& t, std::shared_ptr<Node>& node) {
if (!node) {
std::shared_ptr<Node> newNode = std::make_shared<Node>(tree, t);
node = newNode;
return newNode;
}
if (getComparator()(t, node->value)) {
std::shared_ptr<Node> newLeft = insert(tree, t, node->left);
node->setLeft(newLeft);
}
else {
std::shared_ptr<Node> newRight = insert(tree, t, node->right);
node->setRight(newRight);
}
return node;
}
template <typename T, typename Comparator>
inline std::shared_ptr<typename BinaryTree<T, Comparator>::Node> BinaryTree<T, Comparator>::Node::search (const T& t, std::shared_ptr<Node>& node) {
if (getComparator()(t, node->value))
return search (t, node->left);
else if (getComparator()(node->value, t))
return search (t, node->right);
else // Match is found.
return node;
}
template <typename T, typename Comparator>
inline std::shared_ptr<typename BinaryTree<T, Comparator>::Node> BinaryTree<T, Comparator>::Node::findMaxNode (const std::shared_ptr<Node>& node) const {
if (!node)
return nullptr;
if (!node->right)
return node; // If node has a central node, then return node will still give a node with maximal value since the central node has compares equal to it.
return (findMaxNode(node->right));
}
template <typename T, typename Comparator>
inline std::shared_ptr<typename BinaryTree<T, Comparator>::Node> BinaryTree<T, Comparator>::Node::removeMaxNode (std::shared_ptr<Node>& node, std::shared_ptr<Node>& maxNode) {
if (node == maxNode) {
if (maxNode->left)
maxNode->left->parent = node;
return maxNode->left;
}
std::shared_ptr<Node> newRight = removeMaxNode (node->right, maxNode);
node->setRightMinus(newRight);
return node;
}
template <typename T, typename Comparator>
inline void BinaryTree<T, Comparator>::adjustDepthFromRoot (std::shared_ptr<Node>& node, int adjustment) {
if (!node)
return;
node->depthFromRoot += adjustment;
adjustDepthFromRoot (node->left, adjustment);
adjustDepthFromRoot (node->right, adjustment);
}
template <typename T, typename Comparator>
inline std::shared_ptr<typename BinaryTree<T, Comparator>::Node> BinaryTree<T, Comparator>::Node::remove (const T& t, std::shared_ptr<Node>& node) {
if (getComparator()(t, node->value)) {
std::shared_ptr<Node> newLeft = remove(t, node->left);
node->setLeftMinus(newLeft);
}
else if (getComparator()(node->value, t)) {
std::shared_ptr<Node> newRight = remove(t, node->right);
node->setRightMinus(newRight);
}
else { // Match is found.
if (!node->left) { // node has only the right child, or no child. This might return nullptr if there is no child, but that is what we want anyway.
if (node->isRoot())
binaryTree.replaceRoot(node->right);
binaryTree.decreaseDepthFromRoot(node->right); // Recursive call for the entire subtree rooted at node->right.
return node->right;
}
if (!node->right) { // node has only one child, namely a left child (due to the previous if statement). This this will always return a valid node.
if (node->isRoot())
binaryTree.replaceRoot(node->left);
binaryTree.decreaseDepthFromRoot(node->left); // Recursive call for the entire subtree rooted at node->left.
return node->left;
}
// Node has two children.
std::shared_ptr<Node> maxNode = findMaxNode(node->left), r = removeMaxNode(node->left, maxNode);
maxNode->depthFromRoot = node->depthFromRoot;
binaryTree.decreaseDepthFromRoot(maxNode->left); // Recursive call for the entire subtree rooted at maxNode->left (note that maxNode->right must be nullptr by definition of maxNode).
maxNode->setLeftSimple(r);
maxNode->setRightMinus(node->right);
maxNode->subtreeSize = node->subtreeSize - 1; // Since maxNode is replacing node's position and hence becomes the new root of the subtree that was rooted at 'node' (with maxNode no longer in that subtree, of course).
if (node->isRoot())
binaryTree.replaceRoot(maxNode);
return maxNode;
}
return node;
}
template <typename T, typename Comparator>
inline void BinaryTree<T, Comparator>::display() const {
displayNodeAndChildren(root);
if (root)
std::cout << "Tree Root = " << root->value << "\n\n";
else
std::cout << "There is no root.\n";
}
template <typename T, typename Comparator>
inline void BinaryTree<T, Comparator>::displayNodeAndChildren (const std::shared_ptr<Node>& node) const {
if (!node) {
std::cout << '\n';
return;
}
const T& t = node->value;
std::cout << t << ", subtreeSize = " << node->subtreeSize << ", subtreeHeight = " << node->subtreeHeight << ", depthFromRoot = " << node->depthFromRoot << '\n';
if (node->parent) std::cout << t << "'s parent = " << node->parent->value << '\n'; else std::cout << t << "'s parent = nullptr\n";
std::cout << t << "'s left = ";
displayNodeAndChildren (node->left);
std::cout << t << "'s right = ";
displayNodeAndChildren (node->right);
}
template <typename T, typename Comparator>
BinaryTree<T, Comparator>& BinaryTree<T, Comparator>::operator= (const BinaryTree<T, Comparator>& other) { // BinaryTree assignment operator.
std::cout << "BinaryTree assignment operator called.\n";
if (this == &other)
return *this;
root = std::shared_ptr<Node>(new Node(*other.root));
return *this;
}
template <typename T, typename Comparator>
BinaryTree<T, Comparator>& BinaryTree<T, Comparator>::operator= (BinaryTree<T, Comparator>&& other) { // BinaryTree move assignment operator.
std::cout << "\nBinaryTree move assignment operator called.\n";
if (this == &other)
return *this;
root = other.root;
other.root = nullptr;
return *this;
}
template <typename T, typename Comparator>
BinaryTree<T, Comparator>::Node::Node (const BinaryTree<T, Comparator>::Node& other) : std::enable_shared_from_this<Node>(), binaryTree(other.binaryTree), value(other.value) { // Node copy constructor.
if (other.left) left = std::shared_ptr<Node>(new Node(*other.left)); // Each child invokes the Node copy constructor recursively. New shared_ptrs are created, thereby resulting in a deep BinaryTree copy, rather than a shallow copy.
if (other.right) right = std::shared_ptr<Node>(new Node(*other.right));
}
template <typename T, typename Comparator>
typename BinaryTree<T, Comparator>::Node& BinaryTree<T, Comparator>::Node::operator= (const BinaryTree<T, Comparator>::Node& other) { // Node assignment operator.
if (this == &other)
return *this;
value = other.value;
if (other.left) left = std::shared_ptr<Node>(new Node(*other.left)); // Node copy constructor called. Previously owned node is deleted automatically.
if (other.right) right = std::shared_ptr<Node>(new Node(*other.right));
return *this;
}
template <typename T, typename Comparator>
BinaryTree<T, Comparator>::Node::Node (BinaryTree<T, Comparator>::Node&& other) : std::enable_shared_from_this<Node>(), binaryTree(other.binaryTree), value(other.value),
left(std::shared_ptr<Node>(new Node(*other.left))), right(std::shared_ptr<Node>(new Node(*other.right))) { // Node move constructor.
other.left = nullptr;
other.right = nullptr;
}
template <typename T, typename Comparator>
typename BinaryTree<T, Comparator>::Node& BinaryTree<T, Comparator>::Node::operator= (BinaryTree<T, Comparator>::Node&& other) { // Node move assignment operator.
if (this == &other)
return *this;
value = other.value;
left = std::shared_ptr<Node>(new Node(*other.left));
right = std::shared_ptr<Node>(new Node(*other.right));
other.left = nullptr;
other.right = nullptr;
return *this;
}
template <typename T, typename Comparator>
inline void BinaryTree<T, Comparator>::Node::setLeft (std::shared_ptr<Node>& node) {
const std::size_t formerLeftSubtreeSize = left ? left->subtreeSize : 0;
left = node;
if (node) {
node->parent = this->shared_from_this();
subtreeSize++;
node->depthFromRoot = depthFromRoot + 1; // This line does not appear in Node::setLeftMinus.
const std::size_t h = node->subtreeHeight;
if (right)
subtreeHeight = std::max (right->subtreeHeight, h) + 1;
else
subtreeHeight = h + 1;
}
else {
subtreeSize -= formerLeftSubtreeSize;
subtreeHeight = right ? right->subtreeHeight + 1 : 0;
}
}
template <typename T, typename Comparator>
inline void BinaryTree<T, Comparator>::Node::setRight (std::shared_ptr<Node>& node) {
const std::size_t formerRightSubtreeSize = right ? right->subtreeSize : 0;
right = node;
if (node) {
node->parent = this->shared_from_this();
subtreeSize++;
node->depthFromRoot = depthFromRoot + 1; // This line does not appear in Node::setRightMinus.
const std::size_t h = node->subtreeHeight;
if (left)
subtreeHeight = std::max (left->subtreeHeight, h) + 1;
else
subtreeHeight = h + 1;
}
else {
subtreeSize -= formerRightSubtreeSize;
subtreeHeight = left ? left->subtreeHeight + 1 : 0;
}
}
template <typename T, typename Comparator>
inline void BinaryTree<T, Comparator>::Node::setLeftMinus (std::shared_ptr<Node>& node) {
const std::size_t formerLeftSubtreeSize = left ? left->subtreeSize : 0;
left = node;
if (node) {
node->parent = this->shared_from_this();
subtreeSize--;
const std::size_t h = node->subtreeHeight;
if (right)
subtreeHeight = std::max (right->subtreeHeight, h) + 1;
else
subtreeHeight = h + 1;
}
else {
subtreeSize -= formerLeftSubtreeSize;
subtreeHeight = right ? right->subtreeHeight + 1 : 0;
}
}
template <typename T, typename Comparator>
inline void BinaryTree<T, Comparator>::Node::setRightMinus (std::shared_ptr<Node>& node) {
const std::size_t formerRightSubtreeSize = right ? right->subtreeSize : 0;
right = node;
if (node) {
node->parent = this->shared_from_this();
subtreeSize--;
const std::size_t h = node->subtreeHeight;
if (left)
subtreeHeight = std::max (left->subtreeHeight, h) + 1;
else
subtreeHeight = h + 1;
}
else {
subtreeSize -= formerRightSubtreeSize;
subtreeHeight = left ? left->subtreeHeight + 1 : 0;
}
}
template <typename T, typename Comparator>
void BinaryTree<T, Comparator>::rotateLeft (std::shared_ptr<Node>& node) { // The root of the rotation is 'node', and its right child is the pivot of the rotation. The pivot will rotate counter-clockwise and become the new parent of 'node'.
std::shared_ptr<Node> pivot = node->right;
pivot->subtreeSize = node->subtreeSize;
pivot->depthFromRoot--;
node->subtreeSize--; // Since 'pivot' will no longer be in the subtree rooted at 'node'.
const std::size_t a = pivot->left ? pivot->left->subtreeHeight + 1 : 0; // Need to establish node->heightOfSubtree before pivot->heightOfSubtree is established, since pivot->heightOfSubtree depends on it.
node->subtreeHeight = node->left ? std::max(a, node->left->subtreeHeight + 1) : std::max<std::size_t>(a,1);
if (pivot->right) {
node->subtreeSize -= pivot->right->subtreeSize; // The subtree rooted at 'node' loses the subtree rooted at pivot->right.
pivot->subtreeHeight = std::max (pivot->right->subtreeHeight, node->subtreeHeight) + 1;
}
else
pivot->subtreeHeight = node->subtreeHeight + 1;
node->depthFromRoot++;
decreaseDepthFromRoot(pivot->right); // Recursive call for the entire subtree rooted at pivot->right.
increaseDepthFromRoot(node->left); // Recursive call for the entire subtree rooted at node->left.
pivot->parent = node->parent;
if (pivot->parent) { // pivot's new parent will be its former grandparent, which is not nullptr, so the grandparent must be updated with a new left or right child (depending on whether 'node' was its left or right child).
if (pivot->parent->left == node)
pivot->parent->left = pivot;
else
pivot->parent->right = pivot;
}
node->setRightSimple(pivot->left); // Since pivot->left->value is less than pivot->value but greater than node->value. We use the NoSizeAdjustment version because the 'subtreeSize' values of 'node' and 'pivot' are correct already.
pivot->setLeftSimple(node);
if (node == root) {
root = pivot;
root->parent = nullptr;
}
}
template <typename T, typename Comparator>
void BinaryTree<T, Comparator>::rotateRight (std::shared_ptr<Node>& node) { // The root of the rotation is 'node', and its left child is the pivot of the rotation. The pivot will rotate clockwise and become the new parent of 'node'.
std::shared_ptr<Node> pivot = node->left;
pivot->subtreeSize = node->subtreeSize;
pivot->depthFromRoot--;
node->subtreeSize--; // Since 'pivot' will no longer be in the subtree rooted at 'node'.
const std::size_t a = pivot->right ? pivot->right->subtreeHeight + 1 : 0; // Need to establish node->heightOfSubtree before pivot->heightOfSubtree is established, since pivot->heightOfSubtree depends on it.
node->subtreeHeight = node->right ? std::max(a, node->right->subtreeHeight + 1) : std::max<std::size_t>(a,1);
if (pivot->left) {
node->subtreeSize -= pivot->left->subtreeSize; // The subtree rooted at 'node' loses the subtree rooted at pivot->left.
pivot->subtreeHeight = std::max (pivot->left->subtreeHeight, node->subtreeHeight) + 1;
}
else
pivot->subtreeHeight = node->subtreeHeight + 1;
node->depthFromRoot++;
decreaseDepthFromRoot(pivot->left); // Recursive call for the entire subtree rooted at pivot->left.
increaseDepthFromRoot(node->right); // Recursive call for the entire subtree rooted at node->right.
pivot->parent = node->parent;
if (pivot->parent) { // pivot's new parent will be its former grandparent, which is not nullptr, so the grandparent must be updated with a new left or right child (depending on whether 'node' was its left or right child).
if (pivot->parent->left == node)
pivot->parent->left = pivot;
else
pivot->parent->right = pivot;
}
node->setLeftSimple(pivot->right); // Since pivot->right->value is greater than pivot->value but less than node->value.
pivot->setRightSimple(node);
if (node == root) {
root = pivot;
root->parent = nullptr;
}
}
// Testing
struct StringComparator {
bool operator ()(const std::string& a, const std::string& b) const {return a.compare(b) < 0;}
};
const std::string names[] = {"Mahnoor", "Aiman", "Zarish", "Ifrah", "Zumer", "Theebika", "Zoya", "Nabela", "Afrah", "Ashna", "Saleha", "Bapisha"};
BinaryTree<std::string, StringComparator> makeBinaryTree() {
BinaryTree<std::string, StringComparator> tree;
for (const std::string& s : names)
tree.insert(s);
return tree;
}
template <typename Tree>
void rotateRightTest (Tree& tree, const std::string& name) {
auto node = tree.search(name);
tree.rotateRight(node);
std::cout << "\n'tree.rotateRight(" << name << ");' called.\n\n";
tree.display();
}
template <typename Tree>
void rotateLeftTest (Tree& tree, const std::string& name) {
auto node = tree.search(name);
tree.rotateLeft(node);
std::cout << "\n'tree.rotateLeft(" << name << ");' called.\n\n";
tree.display();
}
int main() {
BinaryTree<std::string, StringComparator> tree = makeBinaryTree();
tree.display();
// tree.remove("Zoya"); std::cout << "\n\nZoya removed:\n"; tree.display(); // Removing a node with no children (everything passed).
// tree.remove("Saleha"); std::cout << "\n\nSaleha removed:\n"; tree.display(); // Removing a node with no children (everything passed).
// tree.remove("Theebika"); std::cout << "\n\nTheebika removed:\n"; tree.display(); // Removing a node with one child (everything passed).
// tree.remove("Aiman"); std::cout << "\n\nAiman removed:\n"; tree.display(); // Removing a node with two children (everything passed).
// tree.remove("Zarish"); std::cout << "\n\nZarish removed:\n"; tree.display(); // Removing a node with two children (everything passed).
// tree.remove("Mahnoor"); std::cout << "\n\nMahnoor removed:\n"; tree.display(); // Removing the root of the tree (everything passed).
// rotateRightTest (tree, "Zarish"); // Everything passed.
// rotateRightTest (tree, "Mahnoor"); // Everything passed.
// rotateLeftTest (tree, "Aiman"); // Everything passed.
rotateLeftTest (tree, "Mahnoor"); // Everything passed.
}