Initial commit for working on red-black tree algorithms
This commit is contained in:
		
							parent
							
								
									49eb64f320
								
							
						
					
					
						commit
						f45e479603
					
				
							
								
								
									
										21
									
								
								cpp/algorithms/trees/redblack/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								cpp/algorithms/trees/redblack/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					###############################################################################
 | 
				
			||||||
 | 
					## Author: Shaun Reed                                                        ##
 | 
				
			||||||
 | 
					## Legal: All Content (c) 2021 Shaun Reed, all rights reserved               ##
 | 
				
			||||||
 | 
					## About: A basic CMakeLists configuration to test RBT implementation        ##
 | 
				
			||||||
 | 
					##                                                                           ##
 | 
				
			||||||
 | 
					## Contact: shaunrd0@gmail.com  | URL: www.shaunreed.com | GitHub: shaunrd0  ##
 | 
				
			||||||
 | 
					##############################################################################
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					cmake_minimum_required(VERSION 3.15)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					project (
 | 
				
			||||||
 | 
					    #[[NAME]]   RedBlackTree
 | 
				
			||||||
 | 
					    VERSION     1.0
 | 
				
			||||||
 | 
					    DESCRIPTION "A project for testing red-black tree algorithms"
 | 
				
			||||||
 | 
					    LANGUAGES   CXX
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					add_library(lib-redblack "redblack.cpp")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					add_executable(test-redblack "driver.cpp")
 | 
				
			||||||
 | 
					target_link_libraries(test-redblack lib-redblack)
 | 
				
			||||||
							
								
								
									
										96
									
								
								cpp/algorithms/trees/redblack/driver.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								cpp/algorithms/trees/redblack/driver.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,96 @@
 | 
				
			|||||||
 | 
					/*#############################################################################
 | 
				
			||||||
 | 
					## Author: Shaun Reed                                                        ##
 | 
				
			||||||
 | 
					## Legal: All Content (c) 2021 Shaun Reed, all rights reserved               ##
 | 
				
			||||||
 | 
					## About: Driver program to test RBT algorithms from MIT intro to algorithms ##
 | 
				
			||||||
 | 
					##                                                                           ##
 | 
				
			||||||
 | 
					## Contact: shaunrd0@gmail.com  | URL: www.shaunreed.com | GitHub: shaunrd0  ##
 | 
				
			||||||
 | 
					###############################################################################
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "redblack.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main (const int argc, const char * argv[])
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  BinarySearchTree testTree;
 | 
				
			||||||
 | 
					  std::vector<int> inputValues {2, 6, 9, 1, 5, 8, 10};
 | 
				
			||||||
 | 
					  // Alternative input values
 | 
				
			||||||
 | 
					//  std::vector<int> inputValues {10, 12, 6, 4, 20, 8, 7, 15, 13};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  std::cout << "Building binary search tree with input: ";
 | 
				
			||||||
 | 
					  for (const auto &value : inputValues) std::cout << value << ", ";
 | 
				
			||||||
 | 
					  std::cout << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (const auto &value : inputValues) testTree.insert(value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  std::cout << "\nInorder traversal: \n";
 | 
				
			||||||
 | 
					  testTree.printInOrder();
 | 
				
			||||||
 | 
					  std::cout << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  std::cout << "Postorder traversal: \n";
 | 
				
			||||||
 | 
					  testTree.printPostOrder();
 | 
				
			||||||
 | 
					  std::cout << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  std::cout << "Preorder traversal: \n";
 | 
				
			||||||
 | 
					  testTree.printPreOrder();
 | 
				
			||||||
 | 
					  std::cout << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  std::cout << "\nMinimum value: " << testTree.findMin()->element << std::endl;
 | 
				
			||||||
 | 
					  std::cout << "Maximum value: " << testTree.findMax()->element << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Test removing a node, printing the result in-order
 | 
				
			||||||
 | 
					  std::cout << "\nRemoving root value...\n";
 | 
				
			||||||
 | 
					  testTree.remove(testTree.getRoot()->element);
 | 
				
			||||||
 | 
					  // Can use inline function to remove a value directly for testing -
 | 
				
			||||||
 | 
					//  testTree.remove(6);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  std::cout << "Inorder traversal: \n";
 | 
				
			||||||
 | 
					  testTree.printInOrder();
 | 
				
			||||||
 | 
					  std::cout << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Test copy constructor
 | 
				
			||||||
 | 
					  std::cout << "\nCloning testTree to testTree2...\n";
 | 
				
			||||||
 | 
					  BinarySearchTree testTree2 = testTree;
 | 
				
			||||||
 | 
					  std::cout << "Inorder traversal of the cloned testTree2: \n";
 | 
				
			||||||
 | 
					  testTree2.printInOrder();
 | 
				
			||||||
 | 
					  std::cout << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Test assignment operator
 | 
				
			||||||
 | 
					  std::cout << "\nCloning testTree to testTree3...\n";
 | 
				
			||||||
 | 
					  BinarySearchTree testTree3;
 | 
				
			||||||
 | 
					  testTree3 = testTree;
 | 
				
			||||||
 | 
					  std::cout << "Inorder traversal of the cloned testTree3 tree: \n";
 | 
				
			||||||
 | 
					  testTree3.printInOrder();
 | 
				
			||||||
 | 
					  std::cout << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Test emptying the BST
 | 
				
			||||||
 | 
					  std::cout << "\nEmptying testTree...\n";
 | 
				
			||||||
 | 
					  testTree.makeEmpty();
 | 
				
			||||||
 | 
					  std::cout << "testTree isEmpty: " << (testTree.isEmpty() ? "true" : "false");
 | 
				
			||||||
 | 
					  std::cout << "\ntestTree2 isEmpty: " << (testTree2.isEmpty() ? "true" : "false");
 | 
				
			||||||
 | 
					  std::cout << "\ntestTree3 isEmpty: " << (testTree3.isEmpty() ? "true" : "false");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Testing integrity of deep copy (cloned) data after deletion of testTree
 | 
				
			||||||
 | 
					  std::cout << "\n\nTesting integrity of previously cloned testTree2...\n";
 | 
				
			||||||
 | 
					  std::cout << "Inorder traversal of the cloned testTree2: \n";
 | 
				
			||||||
 | 
					  testTree2.printInOrder();
 | 
				
			||||||
 | 
					  std::cout << std::endl;
 | 
				
			||||||
 | 
					  std::cout << "Inorder traversal of the cloned testTree3: \n";
 | 
				
			||||||
 | 
					  testTree3.printInOrder();
 | 
				
			||||||
 | 
					  std::cout << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  std::cout << "\nChecking if tree contains value of 6: ";
 | 
				
			||||||
 | 
					  std::cout << (testTree2.contains(6) ? "true" : "false") << std::endl;
 | 
				
			||||||
 | 
					  std::cout << "Checking if tree contains value of 600: ";
 | 
				
			||||||
 | 
					  std::cout << (testTree2.contains(600) ? "true" : "false") << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  std::cout << "\nSuccessor of node with value 6: "
 | 
				
			||||||
 | 
					            << testTree2.successor(testTree2.search(6))->element;
 | 
				
			||||||
 | 
					  std::cout << "\nPredecessor of node with value 6: "
 | 
				
			||||||
 | 
					            << testTree2.predecessor(testTree2.search(6))->element;
 | 
				
			||||||
 | 
					  std::cout << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										414
									
								
								cpp/algorithms/trees/redblack/redblack.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										414
									
								
								cpp/algorithms/trees/redblack/redblack.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,414 @@
 | 
				
			|||||||
 | 
					/*#############################################################################
 | 
				
			||||||
 | 
					## Author: Shaun Reed                                                        ##
 | 
				
			||||||
 | 
					## Legal: All Content (c) 2021 Shaun Reed, all rights reserved               ##
 | 
				
			||||||
 | 
					## About: An example of a red-black tree implementation                      ##
 | 
				
			||||||
 | 
					##        The algorithms in this example are seen in MIT Intro to Algorithms ##
 | 
				
			||||||
 | 
					##                                                                           ##
 | 
				
			||||||
 | 
					## Contact: shaunrd0@gmail.com  | URL: www.shaunreed.com | GitHub: shaunrd0  ##
 | 
				
			||||||
 | 
					##############################################################################
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "redblack.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*******************************************************************************
 | 
				
			||||||
 | 
					* Constructors, Destructors, Operators
 | 
				
			||||||
 | 
					*******************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** BinarySearchTree Copy Assignment Operator
 | 
				
			||||||
 | 
					 * @brief Empty the calling object's root BinaryNode, and copy the rhs data
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param rhs The BST to copy, beginning from its root BinaryNode
 | 
				
			||||||
 | 
					 * @return BinarySearchTree The copied BinarySearchTree object
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					BinarySearchTree& BinarySearchTree::operator=(const 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);
 | 
				
			||||||
 | 
					  return *this;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*  BinaryNode Copy Constructor
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  // Base case, breaks recursion when we hit a null node
 | 
				
			||||||
 | 
					  // + Returns to the previous call in the stack
 | 
				
			||||||
 | 
					  if (toCopy == nullptr) return;
 | 
				
			||||||
 | 
					  // Set the element of this BinaryNode to the value in 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 != nullptr) {
 | 
				
			||||||
 | 
					    left = new BinaryNode(toCopy->left);
 | 
				
			||||||
 | 
					    left->parent = this;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (toCopy->right != nullptr) {
 | 
				
			||||||
 | 
					    right = new BinaryNode(toCopy->right);
 | 
				
			||||||
 | 
					    right->parent = this;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*******************************************************************************
 | 
				
			||||||
 | 
					* Public Member Functions
 | 
				
			||||||
 | 
					*******************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** contains
 | 
				
			||||||
 | 
					 * @brief Determines if value exists within a BinaryNode and its children
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Runs in O( height ) time, given the height of the current BST
 | 
				
			||||||
 | 
					 * + In the worst case, we search for a node at the bottom of the BST
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param value The value to search for within the BST
 | 
				
			||||||
 | 
					 * @param start The root BinaryNode to begin the search
 | 
				
			||||||
 | 
					 * @return true If the value is found within the root node or its children
 | 
				
			||||||
 | 
					 * @return false If the value is not found within the root node or its children
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					bool BinarySearchTree::contains(const int &value, BinaryNode *start) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  // If tree is empty
 | 
				
			||||||
 | 
					  if (start == nullptr) return false;
 | 
				
			||||||
 | 
					    // If x is smaller than our current value
 | 
				
			||||||
 | 
					  else if (value < start->element) return contains(value, start->left);
 | 
				
			||||||
 | 
					    // If x is larger than our current value, check the right node
 | 
				
			||||||
 | 
					  else if (value > start->element) return contains(value, start->right);
 | 
				
			||||||
 | 
					  else return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** makeEmpty
 | 
				
			||||||
 | 
					 * @brief Recursively delete the given root BinaryNode and all of its children
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Runs in O( n ) time, since we need to visit each node in the tree once
 | 
				
			||||||
 | 
					 * + Where n is the total number of nodes in the tree
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param tree The root BinaryNode to delete, along with all child nodes
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void BinarySearchTree::makeEmpty(BinarySearchTree::BinaryNode * & tree)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  // Base case: When all nodes have been deleted, tree is a nullptr
 | 
				
			||||||
 | 
					  // + Breaks from recursion
 | 
				
			||||||
 | 
					  if (tree != nullptr) {
 | 
				
			||||||
 | 
					    makeEmpty(tree->left);
 | 
				
			||||||
 | 
					    makeEmpty(tree->right);
 | 
				
			||||||
 | 
					    delete tree;
 | 
				
			||||||
 | 
					    tree = nullptr;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** 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
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Runs in O( height ) time, since in the worst case we insert the node at the
 | 
				
			||||||
 | 
					 * + bottom of the BST.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param newValue The value to be inserted
 | 
				
			||||||
 | 
					 * @param start The BinaryNode to begin insertion
 | 
				
			||||||
 | 
					 * @param prevNode The last checked BinaryNode
 | 
				
			||||||
 | 
					 * + prevNode is used to initialize new node's parent
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void BinarySearchTree::insert(const int &newValue,
 | 
				
			||||||
 | 
					                              BinaryNode *&start, BinaryNode *prevNode)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  // Base case: We found a valid position which is empty for the newValue
 | 
				
			||||||
 | 
					  if (start == nullptr) {
 | 
				
			||||||
 | 
					    // Build a new node, place it at the current position
 | 
				
			||||||
 | 
					    // + Breaks out of recursion
 | 
				
			||||||
 | 
					    start = new BinaryNode(newValue, nullptr, nullptr, prevNode);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  else if (newValue < start->element) insert(newValue, start->left, start);
 | 
				
			||||||
 | 
					  else if (newValue > start->element) insert(newValue, start->right, start);
 | 
				
			||||||
 | 
					  else return;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** remove
 | 
				
			||||||
 | 
					 * @brief Removes a value from the BST of the given BinaryNode
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Runs in O( height ) time, where findMin() is the limiting function
 | 
				
			||||||
 | 
					 * + remove() and transplant() otherwise run in constant time, without findMin()
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param removeNode The BinaryNode to remove from the BST
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void BinarySearchTree::remove(BinaryNode *removeNode)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  if (removeNode->left == nullptr) {
 | 
				
			||||||
 | 
					    // removeNode has no left node; Replace removeNode with removeNode->right
 | 
				
			||||||
 | 
					    // + It doesn't matter if removeNode->right is nullptr or a valid node
 | 
				
			||||||
 | 
					    // + Since there is no left node, this is the only possible valid transplant
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Transplant the right node and its subtree over this node
 | 
				
			||||||
 | 
					    transplant(removeNode, removeNode->right);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  else if (removeNode->right == nullptr) {
 | 
				
			||||||
 | 
					    // removeNode has no right node; Replace removeNode with removeNode->right
 | 
				
			||||||
 | 
					    // + removeNode->left exists, in this case
 | 
				
			||||||
 | 
					    transplant(removeNode, removeNode->left);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  else {
 | 
				
			||||||
 | 
					    // removeNode has a right and left node, find the next value in-order
 | 
				
			||||||
 | 
					    // + findMin(removeNode->right) returns the next largest value in the BST
 | 
				
			||||||
 | 
					    BinaryNode *minNode = findMin(removeNode->right);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // If the next value in-order is not removeNode->right
 | 
				
			||||||
 | 
					    if (minNode->parent != removeNode) {
 | 
				
			||||||
 | 
					      // replace minNode with the next largest attached value, minNode->right
 | 
				
			||||||
 | 
					      transplant(minNode, minNode->right);
 | 
				
			||||||
 | 
					      // Set minNode->right to the node at removeNode->right
 | 
				
			||||||
 | 
					      // + Update the parent of removeNode->right accordingly in the next line
 | 
				
			||||||
 | 
					      minNode->right = removeNode->right;
 | 
				
			||||||
 | 
					      minNode->right->parent = minNode;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Replace removeNode with the next node in-order
 | 
				
			||||||
 | 
					    // + Update the minNode's left and parent nodes in the following lines
 | 
				
			||||||
 | 
					    transplant(removeNode, minNode);
 | 
				
			||||||
 | 
					    minNode->left = removeNode->left;
 | 
				
			||||||
 | 
					    minNode->left->parent = minNode;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** printInOrder
 | 
				
			||||||
 | 
					 * @brief Uses recursion to output left subtree, root node, then right subtrees
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Runs in O( n ) time, since we need to visit each node in the tree once
 | 
				
			||||||
 | 
					 * + Where n is the total number of nodes within the BST
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param start The root BinaryNode to begin the 'In Order' output
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void BinarySearchTree::printInOrder(BinaryNode *start) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  if(start != nullptr) {
 | 
				
			||||||
 | 
					    printInOrder(start->left);
 | 
				
			||||||
 | 
					    std::cout << start->element << " ";
 | 
				
			||||||
 | 
					    printInOrder(start->right);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** printPostOrder
 | 
				
			||||||
 | 
					 * @brief Uses recursion to output left subtree, right subtree, then the root
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Runs in O( n ) time, since we need to visit each node in the tree once
 | 
				
			||||||
 | 
					 * + Where n is the total number of nodes within the BST
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param start The root BinaryNode to begin the 'Post Order' output
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void BinarySearchTree::printPostOrder(BinaryNode *start) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  if (start != nullptr) {
 | 
				
			||||||
 | 
					    printPostOrder(start->left);
 | 
				
			||||||
 | 
					    printPostOrder(start->right);
 | 
				
			||||||
 | 
					    std::cout << start->element << " ";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** printPreOrder
 | 
				
			||||||
 | 
					 * @brief Uses recursion to output the root, then left subtree, right subtrees
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Runs in O( n ) time, since we need to visit each node in the tree once
 | 
				
			||||||
 | 
					 * + Where n is the total number of nodes within the BST
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param start The root BinaryNode to begin the 'Pre Order' output
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void BinarySearchTree::printPreOrder(BinaryNode *start) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  if (start != nullptr) {
 | 
				
			||||||
 | 
					    std::cout << start->element << " ";
 | 
				
			||||||
 | 
					    printPreOrder(start->left);
 | 
				
			||||||
 | 
					    printPreOrder(start->right);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** search
 | 
				
			||||||
 | 
					 * @brief Search for a given value within a tree or subtree using recursion
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Runs in O( height ) time
 | 
				
			||||||
 | 
					 * + In the worst case, we are searching for a node at the bottom of the BST
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param value The value to search for
 | 
				
			||||||
 | 
					 * @param start The node to start the search from; Can be a subtree
 | 
				
			||||||
 | 
					 * @return A pointer to the BinaryNode containing the value within the BST
 | 
				
			||||||
 | 
					 * + Returns nullptr if the node was not found
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					BinarySearchTree::BinaryNode *BinarySearchTree::search(
 | 
				
			||||||
 | 
					    const int &value, BinaryNode *start) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  // Base case: If BST is empty, or holds the value we are searching for
 | 
				
			||||||
 | 
					  // + Breaks out of recursion
 | 
				
			||||||
 | 
					  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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** findMin
 | 
				
			||||||
 | 
					 * @brief Find the minimum value within the BST of the given BinaryNode
 | 
				
			||||||
 | 
					 *        + This example uses a while loop; findMax uses recursion
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Runs in O( height ) time
 | 
				
			||||||
 | 
					 * + In the worst case, we traverse to to the left-most bottom of the BST
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param start The root BinaryNode to begin checking values
 | 
				
			||||||
 | 
					 * @return A pointer to the BinaryNode which contains the smallest value
 | 
				
			||||||
 | 
					 * + Returns nullptr if BST is empty
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					BinarySearchTree::BinaryNode * BinarySearchTree::findMin(BinaryNode *start) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  // If our tree is empty
 | 
				
			||||||
 | 
					  if (start == nullptr) return nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  while (start->left != nullptr) start = start->left;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // If current node has no smaller children, it is min
 | 
				
			||||||
 | 
					  return start;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** findMax
 | 
				
			||||||
 | 
					 * @brief Find the maximum value within the BST of the given BinaryNode
 | 
				
			||||||
 | 
					 *        + This example uses recursion; findMin uses a while loop
 | 
				
			||||||
 | 
					 *        ++ Both functions can be implemented using a loop or recursion
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Runs in O( height ) time
 | 
				
			||||||
 | 
					 * + In the worst case, we traverse to to the right-most bottom of the BST
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param start The root BinaryNode to begin checking values
 | 
				
			||||||
 | 
					 * @return A pointer to the BinaryNode which contains the largest value
 | 
				
			||||||
 | 
					 * + returns nullptr if BST is empty
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					BinarySearchTree::BinaryNode * BinarySearchTree::findMax(BinaryNode *start) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  // If our tree is empty
 | 
				
			||||||
 | 
					  if (start == nullptr) return nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Base case: If current node has no larger children, it is max; Break recursion
 | 
				
			||||||
 | 
					  if (start->right == nullptr) return start;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Move down the right side of our tree and check again
 | 
				
			||||||
 | 
					  return findMax(start->right);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** predecessor
 | 
				
			||||||
 | 
					 * @brief Finds the previous value in-order from the value at a given startNode
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *  Runs in O( height ) time
 | 
				
			||||||
 | 
					 *  + In the worst case we traverse to the bottom of the BST
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param startNode The node containing the value to find predecessor of
 | 
				
			||||||
 | 
					 * @return The node which is the predecessor of startNode
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					BinarySearchTree::BinaryNode * BinarySearchTree::predecessor(BinaryNode *startNode) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  if (startNode->left != nullptr) return findMax(startNode->left);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // If startNode has a parent, walk up the tree until we reach the top
 | 
				
			||||||
 | 
					  // + If startNode has no parent, we set it to nullptr and return
 | 
				
			||||||
 | 
					  BinaryNode *temp = startNode->parent;
 | 
				
			||||||
 | 
					  while (temp != nullptr && temp->left == startNode) {
 | 
				
			||||||
 | 
					    startNode = temp;
 | 
				
			||||||
 | 
					    temp = temp->parent;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return temp;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** successor
 | 
				
			||||||
 | 
					 * @brief Finds the next value in-order from the value at a given startNode
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *  Runs in O( height ) time
 | 
				
			||||||
 | 
					 *  + In the worst case we traverse to the bottom of the BST
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param startNode The node containing the value to find successor of
 | 
				
			||||||
 | 
					 * @return The node which is the successor of startNode
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					BinarySearchTree::BinaryNode * BinarySearchTree::successor(BinaryNode *startNode) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  // If there is a right subtree, next value in-order is findMin(rightSubtree)
 | 
				
			||||||
 | 
					  if (startNode->right != nullptr) return findMin(startNode->right);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // If startNode has a parent, walk up the tree until we reach the top
 | 
				
			||||||
 | 
					  // + If startNode has no parent, we set it to nullptr and return
 | 
				
			||||||
 | 
					  BinaryNode *temp = startNode->parent;
 | 
				
			||||||
 | 
					  while (temp != nullptr && temp->right == startNode) {
 | 
				
			||||||
 | 
					    startNode = temp;
 | 
				
			||||||
 | 
					    temp = temp->parent;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return temp;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*******************************************************************************
 | 
				
			||||||
 | 
					* Private Member Functions
 | 
				
			||||||
 | 
					*******************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** clone
 | 
				
			||||||
 | 
					 * @brief Clone a BST node and all its children using recursion
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Runs in O( n ) time, since each node must be copied individually
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param start The node to begin cloning from
 | 
				
			||||||
 | 
					 * @return A pointer to the BinaryNode which is root node of the copied tree
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					BinarySearchTree::BinaryNode * BinarySearchTree::clone(BinaryNode *start)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  // Base case: There is nothing to copy, break from recursion
 | 
				
			||||||
 | 
					  if (start == nullptr) return nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Construct all child nodes through recursion, return root node
 | 
				
			||||||
 | 
					  return new BinaryNode(start);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** transplant
 | 
				
			||||||
 | 
					 * @brief Replaces, or overwrites, a node and with a new node
 | 
				
			||||||
 | 
					 * + The subtree attaches to oldNode is replace with that of newNode
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Runs in constant O( 1 ) time
 | 
				
			||||||
 | 
					 * + We only need to check and update values immediately available
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param oldNode The node to overwrite with newNode
 | 
				
			||||||
 | 
					 * @param newNode The new node to take the place of oldNode
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void BinarySearchTree::transplant(BinaryNode *oldNode, BinaryNode *newNode)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  // case 1: If oldNode is the root node at this->root
 | 
				
			||||||
 | 
					  // + 2: if the oldNode is the left child of it's parent
 | 
				
			||||||
 | 
					  // + 3: case if the oldNode  is the right child of  it's parent
 | 
				
			||||||
 | 
					  if (oldNode->parent == nullptr) root = newNode;
 | 
				
			||||||
 | 
					  else if (oldNode == oldNode->parent->left) {
 | 
				
			||||||
 | 
					    // Update the parent of oldNode to reflect the transplant
 | 
				
			||||||
 | 
					    oldNode->parent->left = newNode;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  else if (oldNode == oldNode->parent->right) {
 | 
				
			||||||
 | 
					    // Update the parent of oldNode to reflect the transplant
 | 
				
			||||||
 | 
					    oldNode->parent->right = newNode;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // If we did not replace oldNode with a nullptr, update newNode's parent
 | 
				
			||||||
 | 
					  if (newNode != nullptr) newNode->parent = oldNode->parent;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										83
									
								
								cpp/algorithms/trees/redblack/redblack.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								cpp/algorithms/trees/redblack/redblack.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,83 @@
 | 
				
			|||||||
 | 
					/*#############################################################################
 | 
				
			||||||
 | 
					## Author: Shaun Reed                                                        ##
 | 
				
			||||||
 | 
					## Legal: All Content (c) 2021 Shaun Reed, all rights reserved               ##
 | 
				
			||||||
 | 
					## About: An example of a red black tree implementation                      ##
 | 
				
			||||||
 | 
					##        The algorithms in this example are seen in MIT Intro to Algorithms ##
 | 
				
			||||||
 | 
					##                                                                           ##
 | 
				
			||||||
 | 
					## Contact: shaunrd0@gmail.com  | URL: www.shaunreed.com | GitHub: shaunrd0  ##
 | 
				
			||||||
 | 
					##############################################################################
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef REDBLACK_H
 | 
				
			||||||
 | 
					#define REDBLACK_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: Add balance() method to balance overweight branches
 | 
				
			||||||
 | 
					class BinarySearchTree {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					  // BinaryNode Structure
 | 
				
			||||||
 | 
					  struct BinaryNode{
 | 
				
			||||||
 | 
					    int element;
 | 
				
			||||||
 | 
					    BinaryNode *left, *right, *parent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Ctor for specific element, lhs, rhs
 | 
				
			||||||
 | 
					    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);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  BinarySearchTree() : root(nullptr) {};
 | 
				
			||||||
 | 
					  BinarySearchTree(const BinarySearchTree &rhs) : root(rhs.clone(rhs.root)) {};
 | 
				
			||||||
 | 
					  BinarySearchTree& operator=(const BinarySearchTree& rhs);
 | 
				
			||||||
 | 
					  ~BinarySearchTree() { makeEmpty(root);};
 | 
				
			||||||
 | 
					  inline BinaryNode * getRoot() const { return root;}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Check if value is within the tree or subtree
 | 
				
			||||||
 | 
					  inline bool contains(const int &value) const { return contains(value, root);}
 | 
				
			||||||
 | 
					  bool contains(const int &value, BinaryNode *start) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Empties a given tree or subtree
 | 
				
			||||||
 | 
					  inline void makeEmpty() { makeEmpty(root);}
 | 
				
			||||||
 | 
					  void makeEmpty(BinaryNode *&tree);
 | 
				
			||||||
 | 
					  // Checks if this BST is empty
 | 
				
			||||||
 | 
					  bool isEmpty() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Insert and remove values from a tree or subtree
 | 
				
			||||||
 | 
					  inline void insert(const int &x) { insert(x, root, nullptr);}
 | 
				
			||||||
 | 
					  void insert(const int &newValue, BinaryNode *&start, BinaryNode *prevNode);
 | 
				
			||||||
 | 
					  inline void remove(const int &x) { remove(search(x, root));}
 | 
				
			||||||
 | 
					  void remove(BinaryNode *removeNode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Traversal functions
 | 
				
			||||||
 | 
					  inline void printInOrder() const { printInOrder(root);}
 | 
				
			||||||
 | 
					  inline void printPostOrder() const { printPostOrder(root);}
 | 
				
			||||||
 | 
					  inline void printPreOrder() const { printPreOrder(root);}
 | 
				
			||||||
 | 
					  // Overloaded to specify traversal of a subtree
 | 
				
			||||||
 | 
					  void printInOrder(BinaryNode *start) const;
 | 
				
			||||||
 | 
					  void printPostOrder(BinaryNode *start) const;
 | 
				
			||||||
 | 
					  void printPreOrder(BinaryNode *start) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Find a BinaryNode containing value starting at a given tree / subtree node
 | 
				
			||||||
 | 
					  inline BinaryNode * search(const int &value) const { return search(value, root);}
 | 
				
			||||||
 | 
					  BinaryNode * search(const int &value, BinaryNode *start) const;
 | 
				
			||||||
 | 
					  inline BinaryNode * findMin() const { return findMin(root);}
 | 
				
			||||||
 | 
					  inline BinaryNode * findMax() const { return findMax(root);}
 | 
				
			||||||
 | 
					  // Find nodes with min / max values starting at a given tree / subtree node
 | 
				
			||||||
 | 
					  BinaryNode * findMin(BinaryNode *start) const;
 | 
				
			||||||
 | 
					  BinaryNode * findMax(BinaryNode *start) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  BinaryNode * predecessor(BinaryNode *startNode) const;
 | 
				
			||||||
 | 
					  BinaryNode * successor(BinaryNode *startNode) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					  // BST Private Member Functions
 | 
				
			||||||
 | 
					  static BinaryNode * clone(BinaryNode *start);
 | 
				
			||||||
 | 
					  void transplant(BinaryNode *oldNode, BinaryNode *newNode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  BinaryNode *root;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // REDBLACK_H
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user