From a8b66271350980cd94875ae4e835c195f7f94f5e Mon Sep 17 00:00:00 2001 From: Shaun Reed Date: Wed, 9 Jun 2021 10:16:24 -0400 Subject: [PATCH] Update example of red-black and binary tree algorithms + Use copy-swap idiom for assignment operators + Update and organize CMakeLists subdirectories for algorithm examples --- cpp/algorithms/sorting/CMakeLists.txt | 12 +++--- cpp/algorithms/trees/CMakeLists.txt | 1 + cpp/algorithms/trees/binary/bst.cpp | 47 ++++++++-------------- cpp/algorithms/trees/binary/bst.h | 14 +++++-- cpp/algorithms/trees/redblack/redblack.cpp | 33 ++++++++------- cpp/algorithms/trees/redblack/redblack.h | 4 +- 6 files changed, 51 insertions(+), 60 deletions(-) diff --git a/cpp/algorithms/sorting/CMakeLists.txt b/cpp/algorithms/sorting/CMakeLists.txt index abd1123..cd64a6d 100644 --- a/cpp/algorithms/sorting/CMakeLists.txt +++ b/cpp/algorithms/sorting/CMakeLists.txt @@ -15,12 +15,12 @@ project ( LANGUAGES CXX ) -add_subdirectory(merge) -add_subdirectory(selection) -add_subdirectory(insertion) add_subdirectory(bubble) -add_subdirectory(heap) -add_subdirectory(quick) -add_subdirectory(count) add_subdirectory(bucket) +add_subdirectory(count) +add_subdirectory(heap) +add_subdirectory(insertion) +add_subdirectory(merge) +add_subdirectory(quick) add_subdirectory(radix) +add_subdirectory(selection) diff --git a/cpp/algorithms/trees/CMakeLists.txt b/cpp/algorithms/trees/CMakeLists.txt index 90784f5..896dc44 100644 --- a/cpp/algorithms/trees/CMakeLists.txt +++ b/cpp/algorithms/trees/CMakeLists.txt @@ -16,3 +16,4 @@ project ( ) add_subdirectory(binary) +add_subdirectory(redblack) diff --git a/cpp/algorithms/trees/binary/bst.cpp b/cpp/algorithms/trees/binary/bst.cpp index 7a5fa4a..e7a4440 100644 --- a/cpp/algorithms/trees/binary/bst.cpp +++ b/cpp/algorithms/trees/binary/bst.cpp @@ -17,51 +17,48 @@ *******************************************************************************/ /** BinarySearchTree Copy Assignment Operator - * @brief Empty the calling object's root BinaryNode, and copy the rhs data + * @brief Empty the calling object's root BinaryNode, and swap the rhs data + * + Utilizes the copy-swap-idiom * - * Runs in O( n ) time, since we visit each node in the BST once - * + Where n is the total number of nodes within the BST - * - * makeEmpty() and clone() are both O( n ), and we call each sequentially - * + This would appear to be O( 2n ), but we drop the constant of 2 + * Runs in O( n ) time, since we call makeEmpty() which runs is O( n ) * * @param rhs The BST to copy, beginning from its root BinaryNode - * @return const BinarySearchTree& The copied BinarySearchTree object + * @return BinarySearchTree The copied BinarySearchTree object */ -BinarySearchTree& BinarySearchTree::operator=(const BinarySearchTree &rhs) +BinarySearchTree& BinarySearchTree::operator=(BinarySearchTree rhs) { // If the objects are already equal, do nothing if (this == &rhs) return *this; - // Empty this->root makeEmpty(root); // Copy rhs to this->root - root = clone(rhs.root); + std::swap(root, rhs.root); return *this; } /* BinaryNode Copy Constructor + * @brief Recursively copy rhs node and all child nodes * * Runs in O( n ) time, since we visit each node in the BST once * + Where n is the total number of nodes within the BST * * @param rhs An existing BST to initialize this node (and children) with */ -BinarySearchTree::BinaryNode::BinaryNode(BinaryNode * toCopy) +BinarySearchTree::BinaryNode::BinaryNode(const BinaryNode &rhs) { // Base case, breaks recursion when we hit a null node // + Returns to the previous call in the stack - if (toCopy == nullptr) return; + if (isEmpty(this)) return; // Set the element of this BinaryNode to the value in toCopy->element - element = toCopy->element; + element = rhs.element; // If there is a left / right node, copy it using recursion // + If there is no left / right node, set them to nullptr - if (toCopy->left != nullptr) { - left = new BinaryNode(toCopy->left); + if (rhs.left != nullptr) { + left = new BinaryNode(*rhs.left); left->parent = this; } - if (toCopy->right != nullptr) { - right = new BinaryNode(toCopy->right); + if (rhs.right != nullptr) { + right = new BinaryNode(*rhs.right); right->parent = this; } } @@ -113,19 +110,6 @@ void BinarySearchTree::makeEmpty(BinarySearchTree::BinaryNode * & tree) } } -/** isEmpty - * @brief Determine whether or not the calling BST object is empty - * - * Runs in constant time, O( 1 ) - * - * @return true If this->root node points to an empty tree (nullptr) - * @return false If this->root node points to a constructed BinaryNode - */ -bool BinarySearchTree::isEmpty() const -{ - return root == nullptr; -} - /** insert * @brief Insert a value into the tree starting at a given BinaryNode * + Uses recursion @@ -268,6 +252,7 @@ BinarySearchTree::BinaryNode *BinarySearchTree::search( if (start == nullptr || start->element == value) return start; else if (start->element < value) return search(value, start->right); else if (start->element > value) return search(value, start->left); + else return nullptr; } /** findMin @@ -382,7 +367,7 @@ BinarySearchTree::BinaryNode * BinarySearchTree::clone(BinaryNode *start) if (start == nullptr) return nullptr; // Construct all child nodes through recursion, return root node - return new BinaryNode(start); + return new BinaryNode(*start); } /** transplant diff --git a/cpp/algorithms/trees/binary/bst.h b/cpp/algorithms/trees/binary/bst.h index 0b8db1d..d0f0b2f 100644 --- a/cpp/algorithms/trees/binary/bst.h +++ b/cpp/algorithms/trees/binary/bst.h @@ -27,12 +27,13 @@ public: BinaryNode(const int &el, BinaryNode *lt, BinaryNode *rt, BinaryNode *p) :element(el), left(lt), right(rt), parent(p) {}; // Ctor for a node and any downstream nodes - explicit BinaryNode(BinaryNode * toCopy); + BinaryNode(const BinaryNode &rhs); }; BinarySearchTree() : root(nullptr) {}; - BinarySearchTree(const BinarySearchTree &rhs) : root(rhs.clone(rhs.root)) {}; - BinarySearchTree& operator=(const BinarySearchTree& rhs); + BinarySearchTree(const BinarySearchTree &rhs) : + root(BinarySearchTree::clone(rhs.root)) {}; + BinarySearchTree& operator=(BinarySearchTree rhs); ~BinarySearchTree() { makeEmpty(root);}; inline BinaryNode * getRoot() const { return root;} @@ -44,7 +45,8 @@ public: inline void makeEmpty() { makeEmpty(root);} void makeEmpty(BinaryNode *&tree); // Checks if this BST is empty - bool isEmpty() const; + inline bool isEmpty() const { return isEmpty(root);} + static inline bool isEmpty(const BinaryNode *rhs) { return rhs == nullptr;} // Insert and remove values from a tree or subtree inline void insert(const int &x) { insert(x, root, nullptr);} @@ -70,7 +72,11 @@ public: BinaryNode * findMin(BinaryNode *start) const; BinaryNode * findMax(BinaryNode *start) const; + inline BinaryNode * predecessor(const int &value) const + { return predecessor(search(value));} BinaryNode * predecessor(BinaryNode *startNode) const; + inline BinaryNode * successor(const int &value) const + { return successor(search(value));} BinaryNode * successor(BinaryNode *startNode) const; private: diff --git a/cpp/algorithms/trees/redblack/redblack.cpp b/cpp/algorithms/trees/redblack/redblack.cpp index e0dd3fa..cc8267e 100644 --- a/cpp/algorithms/trees/redblack/redblack.cpp +++ b/cpp/algorithms/trees/redblack/redblack.cpp @@ -27,35 +27,35 @@ RedBlackTree::RedBlackNode *RedBlackTree::nil = new RedBlackTree::RedBlackNode() * * @param rhs An existing RBT to initialize this node (and children) with */ -RedBlackTree::RedBlackNode::RedBlackNode(RedBlackNode * toCopy) +RedBlackTree::RedBlackNode::RedBlackNode(const RedBlackNode &toCopy) { // Base case, breaks recursion when we hit a null node // + Returns to the previous call in the stack - if (toCopy == nil) return; + if (&toCopy == nil) return; // Set the element of this RedBlackNode to the value in toCopy->element - element = toCopy->element; + element = toCopy.element; // If there is a left / right node, copy it using recursion // + If there is no left / right node, set them to nullptr - if (toCopy->left != nil) { - left = new RedBlackNode(toCopy->left); + if (toCopy.left != nil) { + left = new RedBlackNode(*toCopy.left); left->parent = this; } else left = nil; - if (toCopy->right != nil) { - right = new RedBlackNode(toCopy->right); + if (toCopy.right != nil) { + right = new RedBlackNode(*toCopy.right); right->parent = this; } else right = nil; - if (toCopy->parent == nil) parent = nil; + if (toCopy.parent == nil) parent = nil; - // TODO: Fix the copying of the RBT - color = toCopy->color; + color = toCopy.color; } /** RedBlackTree Copy Assignment Operator - * @brief Empty the calling object's root RedBlackNode, and copy the rhs data + * @brief Empty the calling object's root RedBlackNode, swap with the rhs data + * + Utilizes the copy-swap-idiom * * Runs in O( n ) time, since we visit each node in the RBT once * + Where n is the total number of nodes within the RBT @@ -66,17 +66,15 @@ RedBlackTree::RedBlackNode::RedBlackNode(RedBlackNode * toCopy) * @param rhs The RBT to copy, beginning from its root RedBlackNode * @return RedBlackTree The copied RedBlackTree object */ -RedBlackTree& RedBlackTree::operator=(const RedBlackTree &rhs) +RedBlackTree& RedBlackTree::operator=(RedBlackTree rhs) { // If the objects are already equal, do nothing if (this == &rhs) return *this; // Empty this->root makeEmpty(root); - -// if (root == nil) root = new RedBlackNode(); // Copy rhs to this->root - root = clone(rhs.root); + std::swap(root, rhs.root); return *this; } @@ -119,7 +117,7 @@ bool RedBlackTree::contains(const int &value, RedBlackNode *start) const */ void RedBlackTree::rotateLeft(RedBlackNode *pivotNode) { - // To rotateRight, we must relocate the rightChild node + // To rotateLeft, we must relocate the rightChild node RedBlackNode *rightChild = pivotNode->right; pivotNode->right = rightChild->left; @@ -640,6 +638,7 @@ RedBlackTree::RedBlackNode *RedBlackTree::search( if (start == nil || start->element == value) return start; else if (start->element < value) return search(value, start->right); else if (start->element > value) return search(value, start->left); + else return nullptr; } /** findMin @@ -756,7 +755,7 @@ RedBlackTree::RedBlackNode * RedBlackTree::clone(RedBlackNode *start) if (start == nil) return nil; // Construct all child nodes through recursion, return root node - return new RedBlackNode(start); + return new RedBlackNode(*start); } /** transplant diff --git a/cpp/algorithms/trees/redblack/redblack.h b/cpp/algorithms/trees/redblack/redblack.h index f251752..d8113ba 100644 --- a/cpp/algorithms/trees/redblack/redblack.h +++ b/cpp/algorithms/trees/redblack/redblack.h @@ -30,13 +30,13 @@ public: RedBlackNode *lt, RedBlackNode *rt, RedBlackNode *p) :element(el), color(c), left(lt), right(rt), parent(p) {}; // Ctor for copying a node and any downstream nodes - explicit RedBlackNode(RedBlackNode * toCopy); + RedBlackNode(const RedBlackNode &toCopy); }; static RedBlackNode *nil; RedBlackTree() : root(nil) {}; RedBlackTree(const RedBlackTree &rhs);; - RedBlackTree& operator=(const RedBlackTree& rhs); + RedBlackTree& operator=(RedBlackTree rhs); ~RedBlackTree() { makeEmpty(root);}; // Inlined functions provide less verbose interface for using the RBT inline RedBlackNode * getRoot() const { return root;}