Commit 9fed2d1e authored by Rachel Wil Sha Singh's avatar Rachel Wil Sha Singh 💬

Project 3 files

parent 55bd887f
#ifndef _BINARY_SEARCH_TREE_HPP
#define _BINARY_SEARCH_TREE_HPP
#include "BinarySearchTreeNode.hpp"
#include "../Utilities/Logger.hpp"
template <typename TK, typename TD>
//! A template binary search tree class that takes a KEY and a DATA
class BinarySearchTree
{
public:
BinarySearchTree();
~BinarySearchTree();
// Basic functionality
void Push( const TK& newKey, const TD& newData );
bool Contains( const TK& key );
TD& GetData( const TK& key );
// Traversal functions
string GetInOrder();
string GetPreOrder();
string GetPostOrder();
// Additional functionality
TK& GetMinKey();
TK& GetMaxKey();
int GetCount();
int GetHeight();
private:
Node<TK, TD>* FindNode( const TK& key );
// Recursive traversal functions
void RecursiveGetInOrder ( Node<TK, TD>* ptrCurrent, stringstream& stream );
void RecursiveGetPreOrder ( Node<TK, TD>* ptrCurrent, stringstream& stream );
void RecursiveGetPostOrder( Node<TK, TD>* ptrCurrent, stringstream& stream );
// Recursive additional functionality
Node<TK, TD>* RecursiveFindNode( const TK& key, Node<TK, TD>* ptrCurrent );
TK& RecursiveGetMax( Node<TK, TD>* ptrCurrent );
TK& RecursiveGetMin( Node<TK, TD>* ptrCurrent );
int RecursiveGetHeight( Node<TK, TD>* ptrCurrent );
bool RecursiveContains( const TK& key, Node<TK, TD>* ptrCurrent );
void RecursivePush( const TK& newKey, const TD& newData, Node<TK, TD>* ptrCurrent );
private:
//! A pointer to the root node of the tree; TK = data type of the key, TD = data type of the data.
Node<TK, TD>* m_ptrRoot;
//! The amount of nodes in the tree
int m_nodeCount;
//! The tester is our friend
friend class BinarySearchTreeTester;
};
/* **************************************************************************************** */
/* ******************************************************************* Function definitions */
/* **************************************************************************************** */
//! Initializes the member variables of the Binary Search Tree - pointers initialized to nullptr, sizes to 0.
template <typename TK, typename TD>
BinarySearchTree<TK,TD>::BinarySearchTree()
{
Logger::OutHighlight( "FUNCTION BEGIN", "BinarySearchTree<TK,TD> Constructor" );
m_ptrRoot = nullptr;
m_nodeCount = 0;
}
//! Deallocates any allocated memory
template <typename TK, typename TD>
BinarySearchTree<TK,TD>::~BinarySearchTree()
{
Logger::OutHighlight( "FUNCTION BEGIN", "BinarySearchTree<TK,TD> Destructor" );
if ( m_ptrRoot != nullptr )
{
delete m_ptrRoot;
}
}
//! Checks if the tree contains a given unique identifier key.
/**
This is a public "interface" function that calls the recursive version starting at the m_ptrRoot node.
@param key the unique identifier we're searching for.
@return bool true if the tree contains the key, false otherwise.
*/
template <typename TK, typename TD>
bool BinarySearchTree<TK,TD>::Contains( const TK& key )
{
Logger::OutHighlight( "FUNCTION BEGIN", "BinarySearchTree<TK,TD>::Contains" );
return RecursiveContains( key, m_ptrRoot );
}
//! Returns the height of the tree
/**
This is a public "interface" function that calls the recursive version starting at the m_ptrRoot node.
If the m_ptrRoot node is nullptr, then return 0 instead of recursing.
@return int the height of the tree
*/
template <typename TK, typename TD>
int BinarySearchTree<TK,TD>::GetHeight()
{
Logger::OutHighlight( "FUNCTION BEGIN", "BinarySearchTree<TK,TD>::GetHeight" );
throw NotImplementedException( "BinarySearchTree::GetHeight()" ); // TODO: Remove me once you've implemented the function
// TODO: Add your code here
}
//! Returns the data associated with the unique identifier key passed in.
/**
1. Call the FindNode method, passing in the key (or the RecursiveFindNode, passing in the key and m_ptrRoot).
- If this returns nullptr, then throw an exception.
- Otherwise, return the data in this node.
@param key the unique identifier key
@return TD& the data associated with that key
*/
template <typename TK, typename TD>
TD& BinarySearchTree<TK,TD>::GetData( const TK& key )
{
Logger::OutHighlight( "FUNCTION BEGIN", "BinarySearchTree<TK,TD>::GetData" );
throw NotImplementedException( "BinarySearchTree::GetData()" ); // TODO: Remove me once you've implemented the function
// TODO: Add your code here
}
//! Push new data into the tree
/**
This is a public "interface" function that calls the recursive version starting at the m_ptrRoot node.
Calls **RecursivePush**.
1. If the tree already contains this key, throw a runtime_error; the key must be unique.
2. If the m_ptrRoot is nullptr, then we are adding the first node (the root) to the tree.
- Allocate memory for the new node via the m_ptrRoot pointer. Set the node's key and data.
- Increment m_nodeCount.
3. If the m_ptrRoot is NOT nullptr, then we call RecursivePush to find a place to store the data.
- Call RecursivePush, passing in the newKey, newData, and the m_ptrRoot to start at.
@param newKey the unique identifier key under which the item can be found in the tree.
@param newData the data being stored in the tree.
*/
template <typename TK, typename TD>
void BinarySearchTree<TK,TD>::Push( const TK& newKey, const TD& newData )
{
Logger::OutHighlight( "FUNCTION BEGIN", "BinarySearchTree<TK,TD>::Push" );
Logger::Out( "New key: " + StringUtil::ToString( newKey ), "BinarySearchTree<TK,TD>::Push" );
Logger::Out( "New data: " + StringUtil::ToString( newData ), "BinarySearchTree<TK,TD>::Push" );
throw NotImplementedException( "BinarySearchTree::Push()" ); // TODO: Remove me once you've implemented the function
// TODO: Add your code here
}
//! Locates a node with the given unique identifier key, or nullptr if not found.
/**
This is an "interface" function that calls the recursive version starting at the m_ptrRoot node.
Calls **RecursiveFindNode**.
@return Node<TK,TD>* the node with the matching key, or nullptr.
*/
template <typename TK, typename TD>
Node<TK, TD>* BinarySearchTree<TK,TD>::FindNode( const TK& key )
{
Logger::OutHighlight( "FUNCTION BEGIN", "BinarySearchTree<TK,TD>::FindNode" );
throw NotImplementedException( "BinarySearchTree::FindNode()" ); // TODO: Remove me once you've implemented the function
// TODO: Add your code here
}
//! Returns a stream of keys in the tree, in-order.
/**
This is a public "interface" function that calls the recursive version starting at the m_ptrRoot node.
Calls **RecursiveGetInOrder**.
@return string a string of all the keys in the tree, in-order.
*/
template <typename TK, typename TD>
string BinarySearchTree<TK,TD>::GetInOrder() // done
{
Logger::OutHighlight( "FUNCTION BEGIN", "BinarySearchTree<TK,TD>::GetInOrder" );
stringstream stream;
RecursiveGetInOrder( m_ptrRoot, stream );
return stream.str();
}
//! Returns a stream of keys in the tree, pre-order.
/**
This is a public "interface" function that calls the recursive version starting at the m_ptrRoot node.
Calls **RecursiveGetPreOrder**.
@return string a string of all the keys in the tree, pre-order.
*/
template <typename TK, typename TD>
string BinarySearchTree<TK,TD>::GetPreOrder() // done
{
Logger::OutHighlight( "FUNCTION BEGIN", "BinarySearchTree<TK,TD>::GetPreOrder" );
stringstream stream;
RecursiveGetPreOrder( m_ptrRoot, stream );
return stream.str();
}
//! Returns a stream of keys in the tree, post-order.
/**
This is a public "interface" function that calls the recursive version starting at the m_ptrRoot node.
Calls **RecursiveGetPostOrder**.
@return string a string of all the keys in the tree, post-order.
*/
template <typename TK, typename TD>
string BinarySearchTree<TK,TD>::GetPostOrder() // done
{
Logger::OutHighlight( "FUNCTION BEGIN", "BinarySearchTree<TK,TD>::GetPostOrder" );
stringstream stream;
RecursiveGetPostOrder( m_ptrRoot, stream );
return stream.str();
}
//! Returns the largest key in the tree.
/**
This is a public "interface" function that calls the recursive version starting at the m_ptrRoot node.
Calls **RecursiveGetMax**.
@return TK& the largest key in the tree.
*/
template <typename TK, typename TD>
TK& BinarySearchTree<TK,TD>::GetMaxKey() // done
{
Logger::OutHighlight( "FUNCTION BEGIN", "BinarySearchTree<TK,TD>::GetMaxKey" );
return RecursiveGetMax( m_ptrRoot );
}
//! Returns the smallest key in the tree.
/**
This is a public "interface" function that calls the recursive version starting at the m_ptrRoot node.
Calls **RecursiveGetMin**.
@return TK& the smallest key in the tree.
*/
template <typename TK, typename TD>
TK& BinarySearchTree<TK,TD>::GetMinKey() // done
{
Logger::OutHighlight( "FUNCTION BEGIN", "BinarySearchTree<TK,TD>::GetMinKey" );
return RecursiveGetMin( m_ptrRoot );
}
//! Returns the total amount of nodes in the tree.
template <typename TK, typename TD>
int BinarySearchTree<TK,TD>::GetCount() // done
{
Logger::OutHighlight( "FUNCTION BEGIN", "BinarySearchTree<TK,TD>::GetCount" );
return m_nodeCount;
}
/* **************************************************************************************** */
/* ******************************************************************** Recursive functions */
/* **************************************************************************************** */
//! Searches the tree for the node with the given key. Returns true if found, false if not.
/**
- Terminating case 1: If ptrCurrent is nullptr, then return false - there's no way this node can have the key we're looking for.
- Terminating case 2: If ptrCurrent's key matches the key parameter, then return true - there's a match.
- Recursive case: Decide whether to recurse to left child or right child and return the result of that recursion.
- If key is less than ptrCurrent->key, recurse left: `return RecursiveContains( key, ptrCurrent->ptrLeft );`
- If key is greater than ptrCurrent->key, recurse right: `return RecursiveContains( key, ptrCurrent->ptrRight );`
@param key The unique identifier key we're searching for
@param ptrCurrent The pointer to the current node we're investigating.
@return bool (terminating case) true if this node's key matches the parameter key,
(terminating case) false if there are no nodes to recurse into,
(recursive case) return a recurse into RecursiveContains into a child node.
*/
template <typename TK, typename TD>
bool BinarySearchTree<TK,TD>::RecursiveContains( const TK& key, Node<TK, TD>* ptrCurrent )
{
Logger::OutHighlight( "FUNCTION BEGIN", "BinarySearchTree<TK,TD>::RecursiveContains", 2 );
throw NotImplementedException( "BinarySearchTree::RecursiveContains()" ); // TODO: Remove me once you've implemented the function
// TODO: Add your code here
}
//! Searches the tree for the node with the given key. Returns the node.
/**
- Terminating case 1: If ptrCurrent is nullptr, then return nullptr - we've ran out of places to search for the node.
- Terminating case 2: If ptrCurrent's key matches the parameter key, return this node (ptrCurrent).
- Recursive case: Decide whether to recurse to left child or right child and return the result of that recursion.
- key is less than ptrCurrent's key? Recurse left.
- key is greater than ptrCurrent's key? Recurse right.
@param key The unique identifier key we're searching for.
@param ptrCurrent The pointer to the current node we're investigating.
@return Node<TK,TD>* The pointer to the node found with a matching key, or nullptr.
*/
template <typename TK, typename TD>
Node<TK, TD>* BinarySearchTree<TK,TD>::RecursiveFindNode( const TK& key, Node<TK, TD>* ptrCurrent )
{
Logger::OutHighlight( "FUNCTION BEGIN", "BinarySearchTree<TK,TD>::RecursiveFindNode", 2 );
throw NotImplementedException( "BinarySearchTree::RecursiveFindNode()" ); // TODO: Remove me once you've implemented the function
// TODO: Add your code here
}
//! Finds a location for the new key and creates a new node in the tree, once a position is found.
/**
1. If newKey is less than ptrCurrent's key AND ptrCurrent's left child is nullptr, then allocate memory and set up via ptrCurrent->ptrLeft.
2. If newKey is less than ptrCurrent's key AND ptrCurrent's left child is NOT nullptr, then recurse left.
3. If newKey is greater than ptrCurrent's key AND ptrCurrent's right child is nullptr, then allocate memory and set up via ptrCurrent->ptrRight.
4. If newKey is greater than ptrCurrent's key AND ptrCurrent's right child is NOT nullptr, then recurse right.
Don't forget to increment the m_nodeCount.
@param newKey The unique key to add to the tree
@param newData The data to associate with the key
@param ptrCurrent The current node we're investigating
*/
template <typename TK, typename TD>
void BinarySearchTree<TK,TD>::RecursivePush( const TK& newKey, const TD& newData, Node<TK, TD>* ptrCurrent )
{
Logger::OutHighlight( "FUNCTION BEGIN", "BinarySearchTree<TK,TD>::RecursivePush", 2 );
throw NotImplementedException( "BinarySearchTree::RecursivePush()" ); // TODO: Remove me once you've implemented the function
// TODO: Add your code here
}
//! Adds the current node's key to the stream, recurses in-order.
/**
1. If ptrCurrent is nullptr, then return early / do nothing.
2. If ptrCurrent is not nullptr, then:
1. InOrder recurse left
2. Stream out self's key: `stream << ptrCurrent->key;`
3. InOrder recurse right
Streaming to the stream will look like this: `stream << "Output data this way." << endl;`
@param ptrCurrent The current node we're working with.
@param stream The stream we're working with.
*/
template <typename TK, typename TD>
void BinarySearchTree<TK,TD>::RecursiveGetInOrder( Node<TK, TD>* ptrCurrent, stringstream& stream )
{
Logger::OutHighlight( "FUNCTION BEGIN", "BinarySearchTree<TK,TD>::RecursiveGetInOrder", 2 );
throw NotImplementedException( "BinarySearchTree::RecursiveGetInOrder()" ); // TODO: Remove me once you've implemented the function
// TODO: Add your code here
}
//! Adds the current node's key to the stream, recurses pre-order.
/**
1. If ptrCurrent is nullptr, then return early / do nothing.
2. If ptrCurrent is not nullptr, then:
1. Stream out self's key: `stream << ptrCurrent->key;`
2. PreOrder recurse left
3. PreOrder recurse right
Streaming to the stream will look like this: `stream << "Output data this way." << endl;`
@param ptrCurrent The current node we're working with.
@param stream The stream we're working with.
*/
template <typename TK, typename TD>
void BinarySearchTree<TK,TD>::RecursiveGetPreOrder( Node<TK, TD>* ptrCurrent, stringstream& stream )
{
Logger::OutHighlight( "FUNCTION BEGIN", "BinarySearchTree<TK,TD>::RecursiveGetPreOrder", 2 );
throw NotImplementedException( "BinarySearchTree::RecursiveGetPreOrder()" ); // TODO: Remove me once you've implemented the function
// TODO: Add your code here
}
//! Adds the current node's key to the stream, recurses post-order.
/**
1. If ptrCurrent is nullptr, then return early / do nothing.
2. If ptrCurrent is not nullptr, then:
1. PostOrder recurse left
2. PostOrder recurse right
3. Stream out self's key: `stream << ptrCurrent->key;`
Streaming to the stream will look like this: `stream << "Output data this way." << endl;`
@param ptrCurrent The current node we're working with.
@param stream The stream we're working with.
*/
template <typename TK, typename TD>
void BinarySearchTree<TK,TD>::RecursiveGetPostOrder( Node<TK, TD>* ptrCurrent, stringstream& stream )
{
Logger::OutHighlight( "FUNCTION BEGIN", "BinarySearchTree<TK,TD>::RecursiveGetPostOrder", 2 );
throw NotImplementedException( "BinarySearchTree::RecursiveGetPostOrder()" ); // TODO: Remove me once you've implemented the function
// TODO: Add your code here
}
//! Continues to recurse right until there are no more nodes; the right-most node is greatest.
/**
1. If ptrCurrent is nullptr, throw a runtime_error.
2. (Terminating case) If ptrCurrent's right child is nullptr, then return the key of ptrCurrent - it is the node with the largest key.
3. (Recurisve case) Recurse to the right child, returning its result.
@param ptrCurrent The current node we're working with.
@return TK& The key of the largest node.
*/
template <typename TK, typename TD>
TK& BinarySearchTree<TK,TD>::RecursiveGetMax( Node<TK, TD>* ptrCurrent )
{
Logger::OutHighlight( "FUNCTION BEGIN", "BinarySearchTree<TK,TD>::RecursiveGetMax", 2 );
throw NotImplementedException( "BinarySearchTree::RecursiveGetMax()" ); // TODO: Remove me once you've implemented the function
// TODO: Add your code here
}
//! Continues to recurse left until there are no more nodes; the left-most node is smallest.
/**
1. If ptrCurrent is nullptr, throw a runtime_error.
2. (Terminating case) If ptrCurrent's left child is nullptr, then return the key of ptrCurrent - it is the node with the smallest key.
3. (Recurisve case) Recurse to the left child, returning its result.
@param ptrCurrent The current node we're working with.
@return TK& The key of the smallest node.
*/
template <typename TK, typename TD>
TK& BinarySearchTree<TK,TD>::RecursiveGetMin( Node<TK, TD>* ptrCurrent )
{
Logger::OutHighlight( "FUNCTION BEGIN", "BinarySearchTree<TK,TD>::RecursiveGetMin", 2 );
throw NotImplementedException( "BinarySearchTree::RecursiveGetMin()" ); // TODO: Remove me once you've implemented the function
// TODO: Add your code here
}
//! Gets the height of its left and right subtree, takes the highest as the height and adds one to the level.
/**
1. (Terminating case) If ptrCurrent is nullptr, then return 0 - there is no subtree here.
2. Create a variable for the leftHeight, assign it a value by recursing into the left child.
3. Create a variable for the rightHeight, assign it a value by recursing into the right child.
4. Which subtree was taller?
- If the leftHeight is greater than the rightHeight, return leftHeight + 1 as this tree's height.
- Otherwise, return rightHeight + 1 as this tree's height.
@param ptrCurrent The current node we're working with.
@return int The height of the current subtree with its root at ptrCurrent.
*/
template <typename TK, typename TD>
int BinarySearchTree<TK,TD>::RecursiveGetHeight( Node<TK, TD>* ptrCurrent )
{
Logger::OutHighlight( "FUNCTION BEGIN", "BinarySearchTree<TK,TD>::RecursiveGetHeight", 2 );
throw NotImplementedException( "BinarySearchTree::RecursiveGetHeight()" ); // TODO: Remove me once you've implemented the function
// TODO: Add your code here
}
#endif
#ifndef _BINARY_SEARCH_TREE_NODE_HPP
#define _BINARY_SEARCH_TREE_NODE_HPP
#include "../Exceptions/NotImplementedException.hpp"
template <typename TK, typename TD>
//! A template node class, to be used in the BinarySearchTree class.
class Node // done
{
public:
//! Initializes left and right pointers to nullptr.
Node()
{
ptrLeft = nullptr;
ptrRight = nullptr;
}
Node( TK newKey, TD newData )
{
key = newKey;
data = newData;
ptrLeft = nullptr;
ptrRight = nullptr;
}
//! Destroys left and right children, if they are not pointing to nullptr.
~Node()
{
// destroy children
if ( ptrLeft != nullptr ) { delete ptrLeft; }
if ( ptrRight != nullptr ) { delete ptrRight; }
}
//! Pointer to the left child of the node, which is lesser in value
Node<TK, TD>* ptrLeft;
//! Pointer to the right chlid of the node, which is greater in value
Node<TK, TD>* ptrRight;
//! The data stored by the node
TD data;
//! The key of this node
TK key;
//! The tester is our friend
friend class BinarySearchTreeTester;
};
#endif
#ifndef _NOT_IMPLEMENTED_EXCEPTION
#define _NOT_IMPLEMENTED_EXCEPTION
#include <stdexcept>
#include <string>
using namespace std;
class NotImplementedException : public runtime_error
{
public:
NotImplementedException( string functionName )