Предоставленный код представляет собой реализацию двоичного дерева поиска. (BST) в C++специально предназначенный для «программы приюта для домашних животных», которая была основной темой в классе структур данных в течение продолжительного шестимесячного периода.
Этот BST структура служит контейнером для узлов, которые содержат Pet* объекты, при этом использование указателей определяется всеобъемлющими требованиями проекта.
Реализованный код включает в себя некоторые фундаментальные методы, обычно связанные с BST. Эти методы позволяют выполнять такие операции, как вставка, удаление и поиск питомцев по их именам в дереве и многое другое…
Ограничения:
- Должен компилироваться в C++ 11
- Нет шаблонов
- Придется держать
Petуказатели в дереве - Сортировать питомцев по именам
Я хочу знать, есть ли что-то, что я мог бы улучшить, ООП, выбор дизайна, документация по коду, MakeFile (я вставляю один и тот же файл с тех пор, как начал работать с классами) и так далее. Я действительно хочу научиться делать модульные тесты лучше, я думаю, что это отстой, но она хочет, чтобы мы их делали именно так. Любая помощь или идеи будут оценены.
Код должен скомпилироваться, запустив make в терминале он компилируется в prog и после этого ./prog бежать.
ShelterBST.h
/** DOCUMENTATION:
* @author Jakob Balkovec
* @file shelterBST.h -> @headerfile
* @brief ShelterBST class variables and methods declaration
*/
#ifndef SHELTERBST_H
#define SHELTERBST_H
#include <string>
#include <vector>
class ShelterBST {
private:
/** @struct Pet, used to store the age and the naem of a pet in a shelter */
struct Pet {
std::string name;
int age;
Pet(std::string name = "", int age = 0) : name(name), age(age) {} //Constructor
};
/** @struct TreeNode, used to implement a tree
* @members:
* Pet *pet -> pointer to a Pet object
* TreeNode *left -> left child
* TreeNode *right -> right child
*
* @note noth develop in a new subtree if left or right != NULL
*/
struct TreeNode {
struct Pet *pet;
struct TreeNode *left;
struct TreeNode *right;
};
/** @note BST base*/
struct TreeNode *root;
/** @note Methods*/
struct TreeNode* insert(struct TreeNode* &root, Pet *pet);
struct TreeNode* search(struct TreeNode* root, std::string name);
struct TreeNode* findParent(struct TreeNode* root, std::string name);
struct TreeNode* findPredecessor(struct TreeNode* root, std::string name);
struct TreeNode* deleteNode(struct TreeNode* root, std::string name);
struct TreeNode* destroyTree(struct TreeNode* root);
void searchHelper(struct TreeNode* &node1, struct TreeNode* &node2, std::string name);
void inOrder(struct TreeNode* root);
void postOrder(struct TreeNode* root);
void preOrder(struct TreeNode* root);
void copyHelper(TreeNode* root);
int numberOfChildren(struct TreeNode* root, std::string name);
int numberOfNodes(struct TreeNode* root);
int numberOfInternalNodes(struct TreeNode* root);
int numberOfNodesAtLevel(struct TreeNode* root, int level);
int findHeight(struct TreeNode* root);
bool isBalanced(struct TreeNode* root);
void findPredecessorHelper(struct TreeNode* root, std::vector<std::string> &names);
void preOrderHelper(struct TreeNode* root, std::vector<std::string> &names);
void postOrderHelper(struct TreeNode* root, std::vector<std::string> &names);
/** @note Methods END*/
/** @note tests*/
bool testSearch();
bool testSearchB();
bool testInsert();
bool testFindParent();
bool testFindPredecessor();
bool testDeleteNode();
bool testDestroyTree();
bool testNumberOfChildren();
bool testNumberOfNodes();
bool testNumberOfInternalNodes();
bool testHeight();
bool testBalance();
bool testNodesAtLevel();
bool testInOrder();
bool testPreOrder();
bool testPostOrder();
std::vector<bool> runAllTests(std::vector<bool> &testResults);
/** @note tests END*/
public:
/** @note Methods*/
ShelterBST(); //constructor
~ShelterBST(); //destructor
ShelterBST(const ShelterBST& other); //copy constructor
ShelterBST& operator=(const ShelterBST& other);
void inOrderDisplay();
void preOrderDisplay();
void postOrderDisplay();
void insertPet(std::string name, int age);
void findInorderPredecessor(std::string name);
void findParent(std::string name);
void searchPet(std::string name);
void deleteNode(std::string name);
void numberOfNodes();
void numberOfInternalNodes();
void numberOfNodesAtLevel(int level);
void findHeight();
void isBalanced();
void destroyTree();
/** @note Methods END*/
void test();
};
#endif //SHELTER_BST
ShelterBST.cpp
/** DOCUMENTATION:
*
* @author Jakob Balkovec
* @file ShelterBST.cpp [Source code]
* @brief This is the source code file for my ShelterBST class.
* Defines all the private and public methods
* @name Assignment 3
*/
#include <queue>
#include <string>
#include <iostream>
#include <vector>
#include "ShelterBST.h"
#include <unistd.h>
#include <stdexcept>
/** DOCUMENTATION:
* CONSTRUCTOR: @name ShelterBST::ShelterBST()
* @brief
*/
ShelterBST::ShelterBST() {
root = nullptr; /** @note set root to NULL*/
}
/** DOCUMENTATION:
* DESTRUCTOR: @name ShelterBST::~ShelterBST()
* @brief Handles the memory/ frees the resources allocated by the constructor
* @call: calls the destroyTree() method to deallocate all the memory
*/
ShelterBST::~ShelterBST() {
destroyTree(root); //call destroyTree
}
/** DOCUMENTATION:
* COPYCONSTRUCTOR: @name ShelterBST::ShelterBST(const ShelterBST& other)
* @brief Copy constructor for the ShelterBST class.
* @param other The ShelterBST object to be copied.
*/
ShelterBST::ShelterBST(const ShelterBST& other) {
root = nullptr;
copyHelper(other.root);
}
/** DOCUMENTATION:
* OPERATOR: @name ShelterBST& ShelterBST::operator=()
* @brief Copy assignment operator for the ShelterBST class.
* @param other The ShelterBST object to be copied.
* @return The copied ShelterBST object.
*/
ShelterBST& ShelterBST::operator=(const ShelterBST& other) {
if (this != &other) {
destroyTree(root);
root = nullptr;
copyHelper(other.root);
}
return *this;
}
/** DOCUMENTATON:
* PRIVATE: @name void ShelterBST::copyHelper()
* @brief Copies over all the Pets in a tree rooted at root to this tree.
* @param root Pointer to the root of the tree to be copied
*/
void ShelterBST::copyHelper(TreeNode* root) {
if (root == nullptr) {
return;
}
insertPet(root->pet->name, root->pet->age);
copyHelper(root->left);
copyHelper(root->right);
}
/** DOCUMENTATION:
* PRIVATE: @name struct ShelterBST::TreeNode* ShelterBST::insert()
* @brief Inserts a new node with a Pet object into the binary search tree.
* @param root A pointer to the root of the binary search tree.
* @param pet A pointer to the Pet object to be inserted into the binary search tree.
* @return A pointer to the root of the binary search tree, after insertion.
* @invariant This function assumes that the Pet objects in the tree are ordered by name.
*/
struct ShelterBST::TreeNode* ShelterBST::insert(struct TreeNode*& root, Pet *pet) {
/** @note if tree is empty create a new node*/
if(root == nullptr) {
root = new TreeNode;
root -> pet = pet;
/** @note L and R are NULL if just creted*/
root -> right = nullptr;
root -> left = nullptr;
}
/** @note go left*/
else if (pet -> name < root -> pet -> name) {
root->left = insert(root->left, pet);
}
/** @note go right*/
else if (pet -> name > root -> pet -> name) {
root->right = insert(root->right, pet);
}
/** @note handle duplicates*/
/** @bug TEST:*/
else {
TreeNode* existingPet = search(root, pet -> name);
if (existingPet != nullptr) {
std::cout << "\n[A pet with this name already exists in the BST]\n";
return root;
}
}
return root;
}
/** DOCUMENTATION:
* PUBLIC: @name ShelterBST::insertPet()
* @brief Inserts a new pet into the binary search tree
* @param name Name of the pet to be inserted
* @param age Age of the pet to be inserted
* @return void-type
*/
void ShelterBST::insertPet(std::string name, int age) {
root = insert(root, new Pet(name, age));
/** @note handle memory... IN THE END*/
}
/** DOCUMENTATION:
* PRIVATE: @name struct ShelterBST::TreeNode* ShelterBST::search()
* @brief Searches for a node with a Pet object of the given age in the binary search tree.
* @param root A pointer to the root of the binary search tree.
* @param name The name of the Pet object to search for in the binary search tree.
* @return A pointer to the node with the Pet object of the given age, or nullptr if it does not exist in the tree.
* @invariant This function assumes that the Pet objects in the tree are ordered by name.
*/
struct ShelterBST::TreeNode* ShelterBST::search(struct TreeNode* root, std::string name) {
if(root == nullptr || root -> pet -> name == name) {
return root;
}
/** @note go left if greater*/
else if(name < root -> pet -> name) {
return search(root -> left, name);
}
/** @note go right if less*/
else {
return search(root -> right, name);
}
return nullptr;
}
/** DOCUMENTATION:
* PRIVATE: @name struct ShelterBST::TreeNode* ShelterBST::findParent()
* @brief Finds the parent node of a given node in the binary search tree
* @param root Root node of the tree
* @param name Name of the node whose parent is to be found
* @return Pointer to the parent node, or nullptr if no parent is found
* @note This function is called recursively to search for the parent of the given node
*/
/** @bug
* Returns A is the parent of B when inserted in the following order -> B, A, C
*/
struct ShelterBST::TreeNode* ShelterBST::findParent(struct TreeNode* root, std::string name) {
if(root == nullptr) {
return nullptr; /** @note no tree*/
}
if(root -> pet -> name == name) {
return root; /** @note the first node is the one we're looking for*/
/** @note return nullptr*/
}
/** @note left side using a recursive approach*/
if(name < root -> pet -> name && root -> left != nullptr) {
if(root -> left -> pet -> name == name) {
return root;
} else {
return findParent(root -> left, name);
}
/** @note right side using a recursive approach*/
}else if(name > root -> pet -> name && root -> right != nullptr) {
if(root -> right -> pet -> name == name) {
return root;
} else {
return findParent(root -> right, name);
}
}
return nullptr;
}
/** DOCUMENTATION:
* PUBLIC: @name void ShelterBST::searchPet()
* @brief Searches for a pet with the given name in the binary search tree
* @param name Name of the pet to search for
* @return Pointer to the node containing the pet, or nullptr if the pet is not found
*/
void ShelterBST::searchPet(std::string name) {
TreeNode* found = search(root, name);
if(found == nullptr) {
std::cout << "\n\n[[nullptr] was returned -> A Pet with the name " << found -> pet -> name << " is [NOT] in the BST]\n\n";
} else {
std::cout << "\n[A Pet with the name \"" << found->pet->name << "\" [IS] in the BST]\n";
}
}
/** DOCUMENTATION:
* PUBLIC: @name void ShelterBST::findParent()
* @brief Finds the parent node of a pet with the given name in the binary search tree
* @param name Name of the pet whose parent node is to be found
* @return Pointer to the parent node of the pet, or nullptr if the pet is not found or is the root node
*/
void ShelterBST::findParent(std::string name) {
TreeNode* parent = findParent(root, name);
if(parent == nullptr) {
std::cout << "\n[[" << name << "] does not have a parent]\n";
}
std::cout << "\n[The Parent of: [" << name << "] is [" << parent -> pet -> name << "]]\n";
}
/** DOCUMENTATION:
* PRIVATE: @name void ShelterBST::preOrderHelper()
* @brief Recursive helper function that psuhes valeus into a vector
* @param root Pointer to the root node of the current subtree
* @param names Vector of pet names representing the current subtree in in-order traversal
* @remark These functions enable testing later on
*/
void ShelterBST::preOrderHelper(struct TreeNode* root, std::vector<std::string> &names) {
if(root == nullptr) {
return;
} else if(root != nullptr) {
preOrderHelper(root -> left, names);
preOrderHelper(root -> right, names);
names.push_back(root -> pet -> name);
}
}
/** DOCUMENTATION:
* PRIVATE: @name void ShelterBST::postOrderHelper()
* @brief Recursive helper function that psuhes valeus into a vector
* @param root Pointer to the root node of the current subtree
* @param names Vector of pet names representing the current subtree in in-order traversal
* @remark These functions enable testing later on
*/
void ShelterBST::postOrderHelper(struct TreeNode* root, std::vector<std::string> &names) {
if(root == nullptr) {
return;
} else if(root != nullptr) {
names.push_back(root -> pet -> name);
postOrderHelper(root -> left, names);
postOrderHelper(root -> right, names);
}
}
/** DOCUMENTATION:
* PRIVATE: @name void ShelterBST::findPredecessorHelper()
* @brief Recursive helper function for finding the predecessor of a pet with the given name in the binary search tree
* @param root Pointer to the root node of the current subtree
* @param names Vector of pet names representing the current subtree in in-order traversal
*/
void ShelterBST::findPredecessorHelper(struct TreeNode *root, std::vector<std::string> &names) {
/** @brief modifiying the inOrder() traversal so it pushes the values into a vector
* from there we can find the predecessor as stated in the PDF File
*/
if(root == nullptr) {
return;
} else if(root != nullptr) {
findPredecessorHelper(root -> left, names);
names.push_back(root -> pet -> name);
findPredecessorHelper(root -> right, names);
}
}
/** DOCUMENTATION:
* PRIVATE: @name struct ShelterBST::TreeNode* ShelterBST::findPredecessor()
* @brief Finds the predecessor of a node in the BST
* @param root The root of the BST
* @param name The name of the node whose predecessor is to be found
* @return A pointer to the predecessor node in the BST
* @invariant This function assumes that the BST contains the node with the given name
*/
struct ShelterBST::TreeNode* ShelterBST::findPredecessor(struct TreeNode* root, std::string name) {
//traverse in order, push values into a vector, find predecessor
std::vector<std::string> namesInBST; /** @note empty vecotr*/
findPredecessorHelper(root, namesInBST); /** @note pushback*/
/** @note search for the predecessor*/
std::string storedName;
for(long unsigned int i = 0; i < namesInBST.size(); i++) {
if(namesInBST[0] == name) {
return nullptr; /** @note first element's predecessor in NULL*/
}
else if(namesInBST[i] == name) {
storedName = namesInBST[i-1]; /** @note segfaulting here [i-1]*/
}
}
TreeNode* preDecessorNode = search(root, storedName);
return preDecessorNode;
}
/** DOCUMENTATION:
* PUBLIC: @name void ShelterBST::findInorderPredecessor()
* @brief Finds the in-order predecessor of the node with the given name.
* @param name The name of the node whose in-order predecessor is to be found.
* @return void-type
*/
void ShelterBST::findInorderPredecessor(std::string name) {
TreeNode* node = findPredecessor(root, name); /** @note change back to name*/
if(node == nullptr) {
std::cout << "\n[\"" << name << "\" does [NOT] have a predecessor]\n";
return;
} else {
std::cout << "\n[The Predecessor of [" << name << "] is: [" << node -> pet -> name << "]\n";
}
}
/** DOCUMENTATION:
* PRIVATE: @name int ShelterBST::numberOfChildren()
* @brief Counts the number of children of a given node with the given name.
* @param root Pointer to the root node of the BST.
* @param name Name of the pet whose children are to be counted.
* @return The number of children of the node with the given name.
* @note Returns 0 if there is no tree or if the node with the given name is not found.
*/
int ShelterBST::numberOfChildren(struct TreeNode* root, std::string name) {
if(root == nullptr) {
return 0; /** @note no tree*/
}
TreeNode* node = search(root, name);
if(node == nullptr) {
/** @note node not found*/
return 0;
}
int numOfChildren = 0;
if(node -> left != nullptr) {
numOfChildren++;
}
if(node -> right != nullptr) {
numOfChildren++;
}
return numOfChildren;
}
/** DOCUMENTATION:
* PRIVATE: @name struct ShelterBST::TreeNode* ShelterBST::deleteNode()
* @brief Deletes a node with the given name from the tree
* @param root A pointer to the root of the tree.
* @param name The name of the node to delete.
* @return A pointer to the root of the updated tree.
*/
/**
* BUG: Segufaulting if I delete the predecessor before the root
*/
struct ShelterBST::TreeNode* ShelterBST::deleteNode(struct TreeNode* root, std::string name) {
/** @note find predecessor if it has 2 children*/
/** @note delete and link grandchild if only one child*/
/** @note delete if no children*/
/** @note parent is inacessible -> segafulting here*/
int const NO_CHILDREN = 0;
int const ONE_CHILD = 1;
int const TWO_CHILDREN = 2;
/** @note tree is empty*/
if(root == nullptr) {
return root;
}
TreeNode* toBeDeleted = search(root, name);
if(toBeDeleted == nullptr) {
return root; /** @note node not found*/
}
/** @note no children*/
if(numberOfChildren(root, name) == NO_CHILDREN) {
if(toBeDeleted == root) {
delete toBeDeleted;
return nullptr; //idk if thats right
}
TreeNode* parent = findParent(root, toBeDeleted -> pet -> name);
if(toBeDeleted == parent -> left) {
parent -> left = nullptr;
} else {
parent -> right = nullptr;
}
delete toBeDeleted;
}
else if(numberOfChildren(root, name) == ONE_CHILD && !toBeDeleted -> left) { /** @note != nullptr*/
if(toBeDeleted == root) {
TreeNode* temp = root -> right;
delete root;
return temp;
}
TreeNode* parent = findParent(root, toBeDeleted -> pet -> name);
if(toBeDeleted == parent -> left) {
parent -> left = toBeDeleted -> right;
} else {
parent -> right = toBeDeleted -> right;
}
delete toBeDeleted;
}
else if(numberOfChildren(root, name) == ONE_CHILD && !toBeDeleted -> right) {
if(toBeDeleted == root) {
TreeNode* temp = root -> right;
delete root;
return temp;
}
TreeNode* parent = findParent(root, toBeDeleted -> pet -> name);
if(toBeDeleted == parent -> left) {
parent -> left = toBeDeleted -> left;
} else {
parent -> right = toBeDeleted -> left;
}
delete toBeDeleted;
}
else if(numberOfChildren(root, name) == TWO_CHILDREN) {
TreeNode* predecessor = findPredecessor(root, toBeDeleted -> pet -> name);
while(predecessor -> right != nullptr) {
predecessor = predecessor -> right;
toBeDeleted -> pet -> name = predecessor -> pet -> name;
toBeDeleted -> left = deleteNode(toBeDeleted -> left, predecessor -> pet -> name);
}
}
return root;
}
/** DOCUMENTATION:
* PRIVATE: @name struct ShelterBST::TreeNode* ShelterBST::destroyTree()
* @brief Recursively deletes all nodes in the tree.
* @return A null pointer.
* @param root Pointer to the root of the tree.
* @note This is a private member function.
*/
struct ShelterBST::TreeNode* ShelterBST::destroyTree(struct TreeNode* root) {
/** @note using recursion to delete the tree*/
/** @note base case*/
if(root == nullptr) {
return nullptr;
/** @note recursive case*/
} else {
destroyTree(root -> left);
destroyTree(root -> right);
delete root -> pet;
delete root;
return nullptr;
}
return nullptr;
}
/** DOCUMENTATION:
* PUBLIC: @name void ShelterBST::deleteNode()
* @brief Deletes the node with the specified name from the BST
* @param name The name of the pet in the node to be deleted.
*/
void ShelterBST::deleteNode(std::string name) {
TreeNode* deleted = deleteNode(root, name);
if(search(root, deleted -> pet -> name) == nullptr) {
std::cout << "\nNode with the name: [" << name << "] was deleted]\n";
} else {
std::cout << "\n[Soemthing went wrong]\n";
}
}
/** DOCUMENTATION:
* PUBLIC: @name void ShelterBST::destroyTree()
* @brief Calls destroy tree to destroy and deallocated the memory used by the BST
*/
void ShelterBST::destroyTree() {
root = destroyTree(root);
if(root != nullptr) {
std::cout << "\n[Something went wrong]\n";;
}
}
/** DOCUMENTATION:
* PRIVATE: @name void ShelterBST::inOrder()
* @brief Traverses the binary search tree in-order, printing the name and age of each Pet object.
* @param root A pointer to the root of the binary search tree.
* @invariant This function assumes that the Pet objects in the tree are ordered by name.
*/
void ShelterBST::inOrder(struct TreeNode* root) {
if(root == nullptr) {
return; /** @note do nothing*/
}
/** @note traverse*/
else if(root != nullptr) {
/** @note left-sub tree*/
inOrder(root -> left);
/** @note cout data*/
std::cout << "Name: [" << root -> pet -> name << "] " << "Age: [" << root -> pet -> age << "]" << std::endl;
/** @note right sub-tree*/
inOrder(root -> right);
}
}
/** DOCUMENTATION:
* PRIVATE: @name void ShelterBST::postOrder()
* @brief Traverses the binary search tree in post-order, printing the name and age of each Pet object.
* @param root A pointer to the root of the binary search tree.
* @invariant This function assumes that the Pet objects in the tree are ordered by name.
*/
void ShelterBST::postOrder(struct TreeNode* root) {
if(root == nullptr) {
return; /** @note do nothing*/
}
else if(root != nullptr) {
/** @note left sub-tree*/
postOrder(root -> left);
/** @note right sub-tree*/
postOrder(root -> right);
/** @note cout last*/
std::cout << "Name: [" << root -> pet -> name << "] " << "Age: [" << root -> pet -> age << "]" << std::endl;
}
}
/** DOCUMENTATION:
* PRIVATE: @name void ShelterBST::preOrder()
* @brief Traverses the binary search tree in pre-order, printing the name and age of each Pet object.
* @param root A pointer to the root of the binary search tree.
* @invariant This function assumes that the Pet objects in the tree are ordered by name.
*/
void ShelterBST::preOrder(struct TreeNode* root) {
if(root == nullptr) {
return; /** @note do nothing*/
}else if(root != nullptr){
/** @note cout first*/
std::cout << "Name: [" << root -> pet -> name << "] " << "Age: [" << root -> pet -> age << "]" << std::endl;
/** @note left sub-tree*/
postOrder(root -> left);
/** @note right sub-tree*/
postOrder(root -> right);
}
}
/** DOCUMENTATION:
* PUBLIC: @name void ShelterBST::inOrderDisplay()
* @brief Displays the contents of the binary search tree using in-order traversal.
* @call: calls the corresponding private method
*/
void ShelterBST::inOrderDisplay() {
std::cout << "\n[In-Order Traversal]";
std::cout << std::endl << std::endl;
inOrder(root);
}
/** DOCUMENTATION:
* PUBLIC: @name void ShelterBST::preOrderDisplay()
* @brief Displays the contents of the binary search tree using pre-order traversal.
* @call: calls the corresponding private method
*/
void ShelterBST::preOrderDisplay() {
std::cout << "\n[Pre-Order Traversal]";
std::cout << std::endl << std::endl;
preOrder(root);
}
/** DOCUMENTATION:
* PUBLIC: @name void ShelterBST::postOrderDisplay()
* @brief Displays the contents of the binary search tree using post-order traversal.
* @call: calls the corresponding private method
*/
void ShelterBST::postOrderDisplay() {
std::cout << "\n[Post-Order Traversal]";
std::cout << std::endl << std::endl;
postOrder(root);
}
/** DOCUMENTATION:
* PRIVATE: @name int ShelterBST::numberOfNodes()
* @brief Counts the number of nodes in the binary search tree rooted at the given node
* @param root A pointer to the root node of the binary search tree
* @return The number of nodes in the binary search tree rooted at the given node
* @note This is a private member function
*/
int ShelterBST::numberOfNodes(struct TreeNode* root) {
int leftSide = 0;
int rightSide = 0;
if(root == nullptr) {
return 0; /** @note tree is empty*/
}
/** @note right side*/
rightSide = numberOfNodes(root -> right);
/** @note left side*/
leftSide = numberOfNodes(root -> left);
return 1 + leftSide + rightSide; /** @note recursive function call*/
}
/** DOCUMENTATION:
* PRIVATE: @name int ShelterBST::numberOfInternalNodes()
* @brief Returns the number of internal nodes in the binary search tree.
* @param root Pointer to the root node of the tree.
* @return Integer representing the number of internal nodes in the tree.
*/
int ShelterBST::numberOfInternalNodes(struct TreeNode* root) {
if(root == nullptr || (root -> left == nullptr && root -> right == nullptr)) {
/** @note The tree doesn't exist or we reached a leaf node*/
return 0;
}
/** @note +1 cuz of root*/
return 1 + numberOfInternalNodes(root -> left) + numberOfInternalNodes(root -> right);
/** @note recursive call*/
}
/** DOCUMENTATION:
* PRIVATE: @name int ShelterBST::numberOfNodesAtLevel()
* @brief Counts the number of nodes at a given level in the binary search tree
* @param root A pointer to the root node of the binary search tree
* @param level An integer specifying the level at which to count the nodes
* @return An integer specifying the number of nodes at the given level
*/
/** BUG:
* Do I worry about parameter checking(level) -> validation
* Idk if its working
*/
int ShelterBST::numberOfNodesAtLevel(TreeNode* root, int level) {
if (root == nullptr) {
return 0;
} else if (level == 0) {
return 1;
} else {
int count = 0;
if (root->left && root->right) {
count += numberOfNodesAtLevel(root->left, level - 1);
count += numberOfNodesAtLevel(root->right, level - 1);
} else if(root -> right && root -> left == nullptr) {
count += numberOfNodesAtLevel(root -> right, level - 1);
} else if(root -> left && root -> right == nullptr) {
count += numberOfNodesAtLevel(root -> left, level - 1);
}
return count;
}
}
/** DOCUMENTATION:
* PUBLIC: @name void Shelter::BSTnumberOfNodes()
* @brief handles UI
* @return void-type
*/
void ShelterBST::numberOfNodes() {
/** @note handle UI in main*/
std::cout << "\n[The total number of nodes in the BST is: [" << numberOfNodes(root) << "]\n";
}
/** DOCUMENTATION:
* PUBLIC: @name void Shelter::BSTnumberOfInternalNodes()
* @brief handles UI
* @return void-type
*/
void ShelterBST::numberOfInternalNodes() {
std::cout << "\n[The total number of internal nodes in the BST is: [" << numberOfInternalNodes(root) << "]]\n";
}
/** DOCUMENTATION:
* PUBLIC: @name void ShelterBST::numberOfNodesAtLevel()
* @brief handles UI
* @param level initger corrsponding to the level in a binary tree
* @return void-type
*/
void ShelterBST::numberOfNodesAtLevel(int level) {
int nodesAtLevel = numberOfNodesAtLevel(root, level);
std::cout << "\n[The total number of nodes at level [" << level << "] is: [" << nodesAtLevel << "]]\n";
}
/** DOCUMENTATION:
* PRIVATE: @name int ShelterBST::findHeight()
* @brief Calculates the height of the binary search tree starting from the given root node.
* @param root A pointer to the root node of the binary search tree.
* @return The height of the binary search tree.
* @note The height of the binary search tree is defined as the number of edges on the longest
* path from the root node to a leaf node in the tree.
*/
int ShelterBST::findHeight(struct TreeNode* root) {
if (root == nullptr) {
return 0;
}
int leftHeight = findHeight(root->left);
int rightHeight = findHeight(root->right);
return 1 + std::max(leftHeight, rightHeight); /** @note std::max() finds the maximum value in-between the two*/
}
/** DOCUMENTATION:
* PRIVATE: @name bool ShelterBST::isBalanced()
* @brief Check if the binary search tree is balanced.
* @param root The root node of the binary search tree.
* @return True if the binary search tree is balanced, false otherwise.
*/
bool ShelterBST::isBalanced(struct TreeNode* root) {
/** @note find the height of each subtree recursively and compare -> call findHeight with subtrees as params
* can't differ by more than one
*/
if (root == nullptr) {
return true;
}
int leftHeight = findHeight(root->left); /** @note L subtree*/
int rightHeight = findHeight(root->right); /** @note R subtree*/
if (abs(leftHeight - rightHeight) > 1) { /** @note using abs because it cant be negative */
return false;
}
return isBalanced(root->left) && isBalanced(root->right); /** @note true if right and left subtree are balanced*/
}
/** DOCUMENTATION:
* PUBLIC: @name void ShelterBST::findHeight()
* @brief Handles UI
* @return void-type
*/
void ShelterBST::findHeight() {
std::cout << "\n[The height of the tree is: [" << findHeight(root) << "]\n";
}
/** DOCUMENTATION:
* PUBLIC: @name void ShelterBST::isBalanced()
* @brief Handles UI
* @return void-type
*/
void ShelterBST::isBalanced() {
bool balanced = isBalanced(root);
if(balanced) {
std::cout << "\n[BST is balanced]\n";
} else {
std::cout << "\n[BST is [NOT] balanced]\n";
}
}
/** DOCUMENTATION:
* PRIVATE: @name bool ShelterBST::testSearch()
* @brief Tests the search() function by checking if it returns nullptr for non-existent values.
* @return Returns true if the search() function correctly returns nullptr for non-existent values, false otherwise.
* @note Test 1A: Tests for non-existent values
* @post If the value is not found in the BST, search() should return nullptr.
* @note Assumes that user does not enter spaces for names.
*/
bool ShelterBST::testSearch() { //?1
/** @note
* - assuming user doesnt enter spaces for names
* - might not work idk yet
*/
TreeNode* root = nullptr;
TreeNode* node1 = search(root, "12");
TreeNode* node2 = search(root, "18");
TreeNode* node3 = search(root, "11");
TreeNode* node4 = search(root, "4");
TreeNode* node5 = search(root, "3");
std::vector<TreeNode*> nodes;
nodes.push_back(node1);
nodes.push_back(node2);
nodes.push_back(node3);
nodes.push_back(node4);
nodes.push_back(node5);
for(auto node : nodes) { //== to for each loop in python (<3)
if(node != nullptr) {
std::cout << "\n *** TEST1 [Expected [nullptr] but got something else] *** \n";
return false;
}
}
return true;
}
/** DOCUMENTATION:
* PRIVATE: @name bool ShelterBST::testSearchB()
* @brief Private function to test search() method of ShelterBST class.
* @return Returns a boolean value indicating whether the test has passed or not.
*/
bool ShelterBST::testSearchB() { //?2
TreeNode* root = nullptr;
/** @note insert nodes*/
root = insert(root, new Pet("D", 3));
root = insert(root, new Pet("C", 5));
root = insert(root, new Pet("R", 2));
root = insert(root, new Pet("A", 1));
TreeNode* exists = search(root, "D");
TreeNode* exists2 = search(root, "R");
if(exists == nullptr) {
std::cout << "\n*** TEST2 [Expected [" << root << "] but got [nullptr]] *** \n";
return false;
} else if(exists -> pet -> name != "D") {
std::cout << "\n*** TEST2 [Expected [\"D\"] but got " << exists -> pet -> name <<" *** \n";
}
if(exists2 == nullptr) {
std::cout << "\n*** TEST2 [Expected [" << root << "] but got [nullptr]] *** \n";
return false;
} else if(exists2 -> pet -> name != "R") {
std::cout << "\n*** TEST2 [Expected [\"\"] but got " << exists2 -> pet -> name <<" *** \n";
}
/** @note deallocate*/
destroyTree(root);
return true;
}
/** DOCUMENTATION:
* PRIVATE: @name bool ShelterBST::testInsert()
* @brief Private function to test insert() method of ShelterBST class.
* @return Returns a boolean value indicating whether the test has passed or not.m
* @return
*/
bool ShelterBST::testInsert() { //?3
TreeNode* root = nullptr;
/** @note insert nodes*/
root = insert(root, new Pet("D", 1));
root = insert(root, new Pet("B", 2));
root = insert(root, new Pet("A", 3));
root = insert(root, new Pet("C", 4));
root = insert(root, new Pet("F", 5));
root = insert(root, new Pet("G", 6));
root = insert(root, new Pet("E", 7));
/** @note expected tree
D
/ \
B F
/ \ / \
A C E G
*/
if(root -> pet -> name != "D") { //A
std::cout << "\n*** TEST2[A] [Expected [D] but got [" << root -> pet -> name << "]] ***\n";
return false;
}
if(root -> right -> pet -> name != "F") { //B
std::cout << "\n*** TEST2[B] [Expected [F] but got [" << root -> right -> pet -> name << "]] ***\n";
return false;
}
if(root -> right -> left -> pet -> name != "E") { //C
std::cout << "\n*** TEST2[C] [Expected [E] but got [" << root -> right -> left -> pet -> name << "]] ***\n";
return false;
}
if(root -> right -> right -> pet -> name != "G") { //D
std::cout << "\n*** TEST2[D] [Expected [G] but got [" << root -> right -> right -> pet -> name << "]] ***\n";
return false;
}
if(root -> left -> pet -> name != "B") { //E
std::cout << "\n*** TEST2[E] [Expected [B] but got [" << root -> left -> pet -> name << "]] ***\n";
return false;
}
if(root -> left -> left -> pet -> name != "A") { //F
std::cout << "\n*** TEST2[F] [Expected [A] but got [" << root -> left -> left -> pet -> name << "]] ***\n";
return false;
}
if(root -> left -> right -> pet -> name != "C") { //G
std::cout << "\n*** TEST2[G] [Expected [C] but got [" << root -> left -> right -> pet -> name << "]] ***\n";
return false;
}
if(root -> left -> left -> left != nullptr) { //H
std::cout << "\n*** TEST2[H] [Unexpected node found] ***\n";
return false;
}
if(root -> left -> left -> right != nullptr) { //I
std::cout << "\n*** TEST2[I] [Unexpected node found] ***\n";
return false;
}
if(root -> left -> right -> left != nullptr) { //J
std::cout << "\n*** TEST2[J] [Unexpected node found] ***\n";
return false;
}
if(root -> left -> right -> right != nullptr) { //K
std::cout << "\n*** TEST2[K] [Unexpected node found] ***\n";
return false;
}
if(root -> right -> right -> right != nullptr) { //L
std::cout << "\n*** TEST2[L] [Unexpected node found] ***\n";
return false;
}
if(root -> right -> right -> left != nullptr) { //M
std::cout << "\n*** TEST2[M] [Unexpected node found] ***\n";
return false;
}
if(root -> right -> left -> right != nullptr) { //N
std::cout << "\n*** TEST2[N] [Unexpected node found] ***\n";
return false;
}
if(root -> right -> left -> left != nullptr) { //O
std::cout << "\n*** TEST2[O] [Unexpected node found] ***\n";
return false;
}
/** @note deallocate*/
destroyTree(root);
return true;
}
/** DOCUMENTATION:
* PRIVATE: @name bool ShelterBST::testFindParent()
* @brief Private function to test findParent() method of ShelterBST class.
* @return Returns a boolean value indicating whether the test has passed or not.
*/
bool ShelterBST::testFindParent() { //?4
TreeNode* root = nullptr;
Pet* pet1 = new Pet("C", 5);
root = insert(root, pet1);
Pet* pet2 = new Pet("B", 3);
root = insert(root, pet2);
Pet* pet3 = new Pet("D", 8);
root = insert(root, pet3);
Pet* pet4 = new Pet("J", 12);
root = insert(root, pet4);
Pet* pet5 = new Pet("A", 17);
root = insert(root, pet5);
TreeNode* resultOne = findParent(root, "D");
if (resultOne->pet->name != "C") {
return false; /** @test failed*/
std::cout << "\n*** TEST4 [Expected \"C\" but got \"" << resultOne -> pet -> name << "\"] *** \n";
}
if (resultOne == nullptr) {
std::cout << "\n*** TEST4 [Expected " << findParent(root, "C") << " but got [nullptr]] *** \n";
return false; /** @test failed*/
}
destroyTree(root);
return true; /** @test failed*/
}
/** DOCUMENTATION:
* PRIVATE: @name bool ShelterBST::testFindPredecessor()
* @brief Private function to test findPredecessor() method of ShelterBST class.
* @return Returns a boolean value indicating whether the test has passed or not.
*/
bool ShelterBST::testFindPredecessor() { //?5
TreeNode* root = nullptr;
Pet* pet1 = new Pet("C", 5);
root = insert(root, pet1);
Pet* pet2 = new Pet("B", 3);
root = insert(root, pet2);
Pet* pet3 = new Pet("D", 8);
root = insert(root, pet3);
Pet* pet4 = new Pet("J", 12);
root = insert(root, pet4);
Pet* pet5 = new Pet("A", 17);
root = insert(root, pet5);
std::vector<std::string> names;
findPredecessorHelper(root, names);
if (names.size() != 5 || names[0] != "A" || names[1] != "B" || names[2] != "C" || names[3] != "D" || names[4] != "J") {
std::cout << "\n*** TEST5 [Expected <\"C\" \"B\" \"D\" \"J\" \"A\"> but got ";
for(auto name : names) { //for name in names
std::cout << "\"" << name << "\" ";
}
std::cout << ">] ***\n";
return false; /** @test failed*/
}
TreeNode* predecessor = findPredecessor(root, "C");
if(predecessor -> pet -> name != "B") {
std::cout << "\n*** TEST5 [Expected \"B\" but got \"" << predecessor -> pet -> name << "\"] ***\n";
return false;
}
destroyTree(root);
return true;
}
/** DOCUMENTATION:
* PRIVATE: @name bool ShelterBST::testDeleteNode()
* @brief Private function to test deleteNode() method of ShelterBST class.
* @return Returns a boolean value indicating whether the test has passed or not.
*/
bool ShelterBST::testDeleteNode() { //?6
TreeNode* root = nullptr;
root = insert(root, new Pet("cat", 1));
root = insert(root, new Pet("dog", 2));
root = insert(root, new Pet("fish", 3));
root = deleteNode(root, "dog");
if (search(root, "dog") != nullptr) {
std::cout << "\n*** TEST6 [Expected [nullptr] but got [" << search(root, "dog") << "]]\n ***";
return false;
}
root = deleteNode(root, "cat");
if (search(root, "cat") != nullptr) {
std::cout << "\n*** TEST6 [Expected [nullptr] but got [" << search(root, "cat") << "]]\n ***";
return false;
}
root = deleteNode(root, "fish");
if(root != nullptr) {
std::cout << "\n*** TEST6 [Expected [nullptr] but got [" << root << "]] ***\n";
return false;
}
return true;
}
/** DOCUMENTATION:
* PRIVATE: @name bool ShelterBST::testDestroyTree()
* @brief Private function to test destroyTree() method of ShelterBST class.
* @return Returns a boolean value indicating whether the test has passed or not.
*/
bool ShelterBST::testDestroyTree() { //?7
TreeNode* root = nullptr;
root = insert(root, new Pet("D", 3));
root = insert(root, new Pet("B", 2));
root = insert(root, new Pet("A", 2));
root = insert(root, new Pet("C", 2));
root = insert(root, new Pet("F", 2));
root = insert(root, new Pet("G", 2));
root = insert(root, new Pet("E", 2));
root = destroyTree(root);
if (root != nullptr) {
std::cout << "\n*** TEST7 [Expected [nullptr] but got [" << root << "]] ***\n";
return false;
}
return true;
}
/** DOCUMENTATION:
* PRIVATE: @name bool ShelterBST::testNumberOfChildren()
* @brief Private function to test numberOfChildren() method of ShelterBST class.
* @return Returns a boolean value indicating whether the test has passed or not.
*/
bool ShelterBST::testNumberOfChildren() { //?8
TreeNode* root = nullptr;
root = insert(root, new Pet("D", 3));
root = insert(root, new Pet("B", 2));
root = insert(root, new Pet("A", 2));
root = insert(root, new Pet("C", 2));
root = insert(root, new Pet("F", 2));
root = insert(root, new Pet("G", 2));
root = insert(root, new Pet("E", 2));
root = insert(root, new Pet("Z", 2));
int EQnumChildrenOne = 0;
int EQnumChildrenTwo = 1;
int EQnumChildrenThree = 2;
int numChildrenRoot = numberOfChildren(root, "D"); //has 2 children
if (numChildrenRoot != EQnumChildrenThree) {
std::cout << "\n*** TEST8 [Expected [" << EQnumChildrenThree << "] but got [" << numChildrenRoot << "]] ***\n";
return false;
}
int numChildrenLeaf = numberOfChildren(root, "C"); //has zero children
if (numChildrenLeaf != EQnumChildrenOne) {
std::cout << "\n*** TEST8 [Expected [" << EQnumChildrenOne << "] but got [" << numChildrenLeaf << "]] ***\n";
return false;
}
int numChildrenHanging = numberOfChildren(root, "G"); //has one child
if (numChildrenHanging != EQnumChildrenTwo) {
std::cout << "\n*** TEST8 [Expected [" << EQnumChildrenThree << "] but got [" << numChildrenHanging << "]] ***\n";
return false;
}
destroyTree(root);
return true;
}
/** DOCUMENTATION:
* PRIVATE: @name bool ShelterBST::testNumberOfNodes()
* @brief Private function to test numberOfNodes() method of ShelterBST class.
* @return Returns a boolean value indicating whether the test has passed or not.
*/
bool ShelterBST::testNumberOfNodes() { //?9
TreeNode* root = nullptr;
/** @note the tree is degenerate*/
root = insert(root, new Pet("A", 1));
root = insert(root, new Pet("B", 2));
root = insert(root, new Pet("C", 3));
root = insert(root, new Pet("D", 4));
root = insert(root, new Pet("E", 5));
root = insert(root, new Pet("F", 6));
root = insert(root, new Pet("G", 7));
root = insert(root, new Pet("H", 8));
root = insert(root, new Pet("I", 9));
root = insert(root, new Pet("J", 10));
root = insert(root, new Pet("K", 11));
root = insert(root, new Pet("L", 12));
root = insert(root, new Pet("M", 13));
root = insert(root, new Pet("N", 14));
root = insert(root, new Pet("O", 15));
root = insert(root, new Pet("P", 16));
root = insert(root, new Pet("R", 17));
root = insert(root, new Pet("S", 18));
root = insert(root, new Pet("T", 19));
root = insert(root, new Pet("U", 20));
root = insert(root, new Pet("V", 21));
root = insert(root, new Pet("Z", 22));
root = insert(root, new Pet("Q", 23));
int EQnumNodes = 23;
int numNodes = numberOfNodes(root);
if (numNodes != EQnumNodes) {
std::cout << "\n*** TEST9 [Expected [" << EQnumNodes << "] but got [" << numNodes << "]] ***\n";
return false;
}
destroyTree(root);
return true;
}
/** DOCUMENTATION:
* PRIVATE: @name bool ShelterBST::testNumberOfInternalNodes()
* @brief Private function to test numberOfInternalNodes() method of ShelterBST class.
* @return Returns a boolean value indicating whether the test has passed or not.
*/
bool ShelterBST::testNumberOfInternalNodes() { //?10
TreeNode* root = nullptr;
root = insert(root, new Pet("F", 1));
root = insert(root, new Pet("C", 2));
root = insert(root, new Pet("J", 3));
root = insert(root, new Pet("B", 4));
root = insert(root, new Pet("D", 5));
root = insert(root, new Pet("G", 6));
root = insert(root, new Pet("Z", 7));
int EQinternalNodes = 3;
int internalNodes = numberOfInternalNodes(root);
if(EQinternalNodes != internalNodes) {
std::cout << "\n*** TEST10 [Expected [" << EQinternalNodes << "] but got [" << internalNodes << "]] ***\n";
return false;
}
destroyTree(root);
return true;
}
/** DOCUMENTATION:
* PRIVATE: @name bool ShelterBST::testHeight()
* @brief Private function to test findHeight() method of ShelterBST class.
* @return Returns a boolean value indicating whether the test has passed or not.
*/
bool ShelterBST::testHeight() { //?11
TreeNode* root = nullptr;
/** @note the tree is degenarate/sparse*/
root = insert(root, new Pet("A", 1));
root = insert(root, new Pet("B", 2));
root = insert(root, new Pet("C", 3));
root = insert(root, new Pet("D", 4));
root = insert(root, new Pet("E", 5));
root = insert(root, new Pet("F", 6));
root = insert(root, new Pet("G", 7));
root = insert(root, new Pet("H", 8));
root = insert(root, new Pet("I", 9));
root = insert(root, new Pet("J", 10));
root = insert(root, new Pet("K", 11));
root = insert(root, new Pet("L", 12));
root = insert(root, new Pet("M", 13));
root = insert(root, new Pet("N", 14));
root = insert(root, new Pet("O", 15));
root = insert(root, new Pet("P", 16));
root = insert(root, new Pet("Q", 17));
root = insert(root, new Pet("R", 18));
root = insert(root, new Pet("S", 19));
root = insert(root, new Pet("T", 20));
root = insert(root, new Pet("U", 21));
root = insert(root, new Pet("V", 22));
root = insert(root, new Pet("W", 23));
root = insert(root, new Pet("X", 24));
root = insert(root, new Pet("Y", 25));
root = insert(root, new Pet("Z", 26));
int EQheight = 26;
int height = findHeight(root);
if(EQheight != height) {
std::cout << "\n*** TEST11 [Expected [" << EQheight << "] but got [" << height << "]] *** \n";
return false;
}
destroyTree(root);
return true;
}
/** DOCUMENTATION:
* PRIVATE: @name bool ShelterBST::testBalance()
* @brief Private function to test isBalanced() method of ShelterBST class.
* @return Returns a boolean value indicating whether the test has passed or not.
*/
bool ShelterBST::testBalance() { //?12
TreeNode* root = nullptr;
root = insert(root, new Pet("F", 1));
root = insert(root, new Pet("C", 2));
root = insert(root, new Pet("J", 3));
root = insert(root, new Pet("B", 4));
root = insert(root, new Pet("D", 5));
root = insert(root, new Pet("G", 6));
root = insert(root, new Pet("Z", 7));
bool EQbalance = true;
bool balance = isBalanced(root);
if(EQbalance != balance) {
/** @note boolalpha -> prints true and flase isnstead of 1 and 0*/
std::cout << std::boolalpha << "\n *** TEST12 [Expected [" << EQbalance << "] but got [" << balance << "]] ***\n";
return false;
}
destroyTree(root);
return true;
}
/** DOCUMENTATION:
* PRIVATE: @name bool ShelterBST::testNodesAtLevel()
* @brief Private function to test nodesAtLevel() method of ShelterBST class.
* @return Returns a boolean value indicating whether the test has passed or not.
*/
bool ShelterBST::testNodesAtLevel() { //?13
TreeNode* root = nullptr;
root = insert(root, new Pet("F", 1));
root = insert(root, new Pet("C", 2));
root = insert(root, new Pet("J", 3));
root = insert(root, new Pet("B", 4));
root = insert(root, new Pet("D", 5));
root = insert(root, new Pet("G", 6));
root = insert(root, new Pet("R", 13));
root = insert(root, new Pet("Z", 7));
int EQnodesAtLevelZero = 1; //root
int EQnodesAtLevelOne = 2; //root children
int EQnodesAtLevelTwo = 4; //full -> perfect tree, splits to two more
int EQnodesAtLevelThree = 1; //Z
int nodesAtLevelZero = numberOfNodesAtLevel(root, 0);
int nodesAtLevelOne = numberOfNodesAtLevel(root, 1);
int nodesAtLevelTwo = numberOfNodesAtLevel(root, 2);
int nodesAtLevelThree = numberOfNodesAtLevel(root, 3);
if(EQnodesAtLevelZero != nodesAtLevelZero) {
std::cout << "\n*** TEST13[A] [Expected [" << EQnodesAtLevelZero << "] but got [" << nodesAtLevelZero << "]] ***\n";
return false;
}
if(EQnodesAtLevelOne != nodesAtLevelOne) {
std::cout << "\n*** TEST13[B] [Expected [" << EQnodesAtLevelOne << "] but got [" << nodesAtLevelOne << "]] ***\n";
return false;
}
if(EQnodesAtLevelTwo != nodesAtLevelTwo) {
std::cout << "\n*** TEST13[C] [Expected [" << EQnodesAtLevelTwo << "] but got [" << nodesAtLevelTwo << "]] ***\n";
return false;
}
if(EQnodesAtLevelThree != nodesAtLevelThree) {
std::cout << "\n*** TEST13[D] [Expected [" << EQnodesAtLevelThree << "] but got [" << nodesAtLevelThree << "]] ***\n";
return false;
}
destroyTree(root);
return true;
}
/** DOCUMENTATION:
* PRIVATE: @name bool ShelterBST::testInOrder()
* @brief Private function to test inOrder() method of ShelterBST class.
* @return Returns a boolean value indicating whether the test has passed or not.
*/
bool ShelterBST::testInOrder() { //?14
TreeNode* root = nullptr;
root = insert(root, new Pet("F", 1));
root = insert(root, new Pet("C", 2));
root = insert(root, new Pet("J", 3));
root = insert(root, new Pet("B", 4));
root = insert(root, new Pet("D", 5));
root = insert(root, new Pet("G", 6));
root = insert(root, new Pet("Z", 7));
std::vector<std::string> names;
findPredecessorHelper(root, names);
std::vector<std::string> EQ = {"B", "C", "D", "F", "G", "J", "Z"};
if(names == EQ) { // == operator defined int the vector class (no need to iterate)
return true;
} else {
for(auto i = 0; i < (int)names.size(); i++) {
if(names[i] != EQ[i]) {
std::cout << "\n*** TEST14 [Expected [" << EQ[i] << "] but got [" << names[i] << "]] ***\n";
return false;
}
}
}
destroyTree(root);
return true;
}
/** DOCUMENTATION:
* PRIVATE: @name bool ShelterBST::testPreOrder()
* @brief Private function to test preOrder() method of ShelterBST class.
* @return Returns a boolean value indicating whether the test has passed or not.
*/
bool ShelterBST::testPreOrder() { //?15
TreeNode* root = nullptr;
root = insert(root, new Pet("F", 1));
root = insert(root, new Pet("C", 2));
root = insert(root, new Pet("J", 3));
root = insert(root, new Pet("B", 4));
root = insert(root, new Pet("D", 5));
root = insert(root, new Pet("G", 6));
root = insert(root, new Pet("Z", 7));
std::vector<std::string> names;
preOrderHelper(root, names);
std::vector<std::string> EQ = {"B", "D", "C", "G", "Z", "J", "F"};
if(names == EQ) { // == operator defined int the vector class (no need to iterate)
return true;
} else {
for(auto i = 0; i < (int)names.size(); i++) {
if(names[i] != EQ[i]) {
std::cout << "\n*** TEST14 [Expected [" << EQ[i] << "] but got [" << names[i] << "]] ***\n";
return false;
}
}
}
destroyTree(root);
return true;
}
/** DOCUMENTATION:
* PRIVATE: @name bool ShelterBST::testPostOrder()
* @brief Private function to test postOrder() method of ShelterBST class.
* @return Returns a boolean value indicating whether the test has passed or not.
*/
bool ShelterBST::testPostOrder() { //?16
TreeNode* root = nullptr;
root = insert(root, new Pet("F", 1));
root = insert(root, new Pet("C", 2));
root = insert(root, new Pet("J", 3));
root = insert(root, new Pet("B", 4));
root = insert(root, new Pet("D", 5));
root = insert(root, new Pet("G", 6));
root = insert(root, new Pet("Z", 7));
std::vector<std::string> names;
postOrderHelper(root, names);
std::vector<std::string> EQ = {"F", "C", "B", "D", "J", "G", "Z"}; //Z
if(names == EQ) { // == operator defined int the vector class (no need to iterate)
return true;
} else {
for(auto i = 0; i < (int)names.size(); i++) {
if(names[i] != EQ[i]) {
std::cout << "\n*** TEST16 [Expected [" << EQ[i] << "] but got [" << names[i] << "]] ***\n";
return false;
}
}
}
destroyTree(root);
return true;
}
/** DOCUMENT:
* PRIVATE: @name std::vector<bool> ShelterBST::runAllTests()
* @brief Runs all tests on the ShelterBST object and returns a map of the test results.
* @return An unordered map with the test results. The keys are strings representing the test names,
* and the values are booleans indicating whether each test passed (true) or failed (false).
*/
std::vector<bool> ShelterBST::runAllTests(std::vector<bool> &testResults) {
testResults.push_back(testSearch());
testResults.push_back(testSearchB());
testResults.push_back(testInsert());
testResults.push_back(testFindParent());
testResults.push_back(testFindPredecessor());
testResults.push_back(testDeleteNode());
testResults.push_back(testDestroyTree());
testResults.push_back(testNumberOfChildren());
testResults.push_back(testNumberOfNodes());
testResults.push_back(testNumberOfInternalNodes());
testResults.push_back(testHeight());
testResults.push_back(testBalance());
testResults.push_back(testNodesAtLevel());
testResults.push_back(testInOrder());
testResults.push_back(testPreOrder());
testResults.push_back(testPostOrder());
return testResults;
}
/** DOCUMENTATION:
* PUBLIC: @name void ShelterBST::test()
* @brief Runs tests for all the funcntions
*/
void ShelterBST::test() {
using std::vector;
#define PASS true //macro preprocessor directive
#define FAIL false
std::string const TEST = "*** TEST ";
std::string const PASSED = " PASSED ***";
std::string const FAILED = " - FAILED ###";
std::string const PASSED_2 = " PASSED ***";
std::string const FAILED_2 = " - FAILED ###";
long int const PAUSE = 1000000;
vector<bool> testValues;
std::cout << "\n\n*** RUNNING ALL TESTS *** ";
std::cout << std::flush; //flush the buffer or else usleep doesn't work
usleep(PAUSE);
runAllTests(testValues);
std::cout << std::endl << std::endl;
for(auto i = 0; i < (int)testValues.size(); i++) {
if(testValues[i] == PASS && i < 9) { /** @bug fail and pass*/
std::cout << TEST << i+1 << PASSED << std::endl;
} else if(testValues[i] == FAIL && i < 9) {
std::cout << TEST << i+1 << FAILED << std::endl;
} else if(testValues[i] == PASS && i > 9) {
std::cout << TEST << i+1 << PASSED_2 << std::endl;
} else if(testValues[i] == FAIL && i > 9) {
std::cout << TEST << i+1 << FAILED_2 << std::endl;
}
}
}
assignment3.cpp
/** DOCUMENTATION:
* @author Jakob Balkovec
* @file assignment3.cpp [Driver Code]
* @note Driver code for A3
*
* @brief This assigment focuses on using multiple operations regarding BST like:
* - Insertion
* - Traversals
* - Searching
* - Deletion
*
* Those operations were implemented using a ShelterBST class that includes a struct for Pets and
* A struct for the TreeNodes.
*/
#include <string>
#include <iostream>
#include "ShelterBST.h"
#include <vector>
#include <limits>
#pragma message("[" __FILE__"] " "Last compiled on [" __DATE__ "] at [" __TIME__"]")
/** @name OPID (opeartion ID)
* @enum for the switch statement
* @brief Every operation has a numerical value
*/
enum OPID {
ID_1 = 1,
ID_2 =2,
ID_3 = 3,
ID_4 = 4,
ID_5 = 5,
ID_6 = 6,
ID_7 = 7,
ID_8 = 8,
ID_9 = 9,
ID_10 = 10,
ID_11 = 11,
ID_12 = 12,
ID_13 = 13,
ID_14 = 14,
ID_15 = 15,
ID_16 = 16
};
/** @name printTitle()
* @brief The function prints the title
* @remark Handles UI
* @return void-type
*/
void printTitle() {
std::cout << "\n\n*** [binary search tree]. *** \n\n";
std::cout << "[select one of the following]\n\n";
}
/** @name printMenu()
* @brief The function prints a menu for the user
* @remark Handles UI
* @return void-type
*/
void printMenu() {
std::cout << "\n\n"
<< "[1] [in-order traversal]\n"
<< "[2] [pre-order traversal]\n"
<< "[3] [post-order traversal]\n"
<< "[4] [insert a pet]\n"
<< "[5] [find the predecessor]\n"
<< "[6] [find the parent]\n"
<< "[7] [search for a pet]\n"
<< "[8] [delete a node]\n"
<< "[9] [number of nodes]\n"
<< "[10] [number of internal nodes]\n"
<< "[11] [number of nodes per level]\n"
<< "[12] [get the height]\n"
<< "[13] [BST balance]\n"
<< "[14] [destroy BST]\n"
<< "[15] [quit]\n"
<< "[16] [run-tests]\n";
}
/** @name goodbye()
* @brief The function prompts the user goodbye
* @remark Handles UI
* @return void-type
*/
void goodbye() {
std::cout << "\n\nGoodbye!\n\n";
}
/** @name getInput()
* @brief The function prompts the user to enter a number corresponding to the menu
* @return int choice (0 < choice < 8)
*/
int getInput() {
int const MIN = 1;
int const MAX = 17;
int choice = 0;
std::cout << "\n[Enter]: ";
while (true) {
try {
std::cin >> choice;
if (std::cin.fail()) { //std::cin.fail() if the input is not an intiger returns true
/// @link https://cplusplus.com/forum/beginner/2957/
std::cin.clear(); // clear error flags
std::cin.ignore(10000, '\n'); // ignore up to 10000 characters or until a newline is encountered
throw std::invalid_argument("[Invalid input]");
}
else if (choice < MIN || choice > MAX) {
throw std::out_of_range("[Input out of range. Please enter an integer between 1 and 16]");
}
else {
return choice;
}
}
catch (const std::exception& error) {
std::cout << error.what() << std::endl;
std::cout << "[Re-enter]: ";
}
}
}
/** @name getName()
* @brief Prompts the user to enter a name
* @param name passed by reference
* @return std::string name
*/
std::string getName(std::string &name) {
std::cout << "[Enter Name]: ";
while (true) {
try {
std::cin >> name;
if (std::cin.fail()) {
throw std::runtime_error("[Invalid input. Please enter a valid name]");
}
return name;
} catch (const std::runtime_error& e) {
std::cerr << "Error: " << e.what() << std::endl;
std::cin.clear();
std::cin.ignore(10000, '\n');
}
}
}
/** @name getAge()
* @brief Prompts the user to enter an age
* @param age passed by reference
* @return int age
*/
int getAge(int &age) {
std::cout << "\n[Enter Age]: ";
while (true) {
try {
std::cin >> age;
if (std::cin.fail() || age < 0) {
throw std::runtime_error("[Invalid input. Please enter a positive integer]");
}
return age;
} catch (const std::runtime_error& e) {
std::cerr << "Error: " << e.what() << std::endl;
std::cin.clear();
std::cin.ignore(10000, '\n');
std::cout << "\n[Enter Age]: ";
}
}
}
/** @name getLevel()
* @brief Prompts the user to enter a level
* @param level passed by reference
* @return int level
*/
int getLevel(int &level) { /** @note should I check for user input here -> ???*/
std::cout << "\n[Enter Level]: ";
while (true) {
try {
std::cin >> level;
if (std::cin.fail()) { //std::cin.fail() if the input is not an intiger returns true
/// @link https://cplusplus.com/forum/beginner/2957/
std::cin.clear(); // clear error flags
std::cin.ignore(10000, '\n'); // ignore up to 10000 characters or until a newline is encountered
throw std::invalid_argument("[Invalid input]");
}else {
return level;
}
}
catch (const std::exception& error) {
std::cout << error.what() << std::endl;
std::cout << "[Re-enter]: ";
}
}
}
int main(int argc, char **argv) {
/** DECLARATIONS: */
ShelterBST tree;
std::string name = "";
int age = 0;
int level = 0;
/** DECLARATIONS: */
printTitle();
while(true) {
printMenu();
switch(getInput()) {
case OPID::ID_1: {
/** @note in-order*/
tree.inOrderDisplay();
break;
}
case OPID::ID_2: {
/** @note pre-order*/
tree.preOrderDisplay();
break;
}
case OPID::ID_3: {
/** @note post-order*/
tree.postOrderDisplay();
break;
}
case OPID::ID_4: {
/** @note insert a pet*/
tree.insertPet(getName(name), getAge(age));
break;
}
case OPID::ID_5: {
/** @note find predecessor*/
tree.findInorderPredecessor(getName(name));
break;
}
case OPID::ID_6: {
/** @note find parent*/
tree.findParent(getName(name));
break;
}
case OPID::ID_7: {
/** @note search for a pet*/
tree.searchPet(getName(name));
break;
}
case OPID::ID_8: {
/** @note delte a node*/
tree.deleteNode(getName(name));
break;
}
case OPID::ID_9: {
/** @note number of nodes in BST*/
tree.numberOfNodes();
break;
}
case OPID::ID_10: {
/** @note number of internal nodes*/
tree.numberOfInternalNodes();
break;
}
case OPID::ID_11: {
/** @note number of nodes @ level*/
tree.numberOfNodesAtLevel(getLevel(level));
break;
}
case OPID::ID_12: {
/** @note get height*/
tree.findHeight();
break;
}
case OPID::ID_13: {
/** @note is the BST balanced*/
tree.isBalanced();
break;
}
case OPID::ID_14: {
/** @note destroy the tree*/
tree.destroyTree();
break;
}
case OPID::ID_15: {
/** @note quit*/
tree.destroyTree();
goodbye();
exit(EXIT_FAILURE);
break;
}
case OPID::ID_16: {
/** @note run tests*/
tree.test();
break;
}
default: {
/** @note do nothing...*/
}
}
}
return EXIT_SUCCESS;
}
MakeFile
CC=g++
CFLAGS= -O3 -Wall -Werror -pedantic -std=c++11
SRCS=ShelterBST.cpp assignment3.cpp
OBJS=$(subst .cpp,.o,$(SRCS))
all: prog
prog: $(OBJS)
$(CC) $(CFLAGS) $(OBJS) -o prog
%.o: %.cpp
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f $(OBJS) prog
1 ответ
Вещи, которые вы бы не стали делать в реальном коде
Я знаю, что у вас есть некоторые ограничения, однако в коде, который вы пишете вне курса, у вас не будет этих ограничений, и вы будете действовать по-другому. Я просто хочу указать на это для читателей:
- С++ 11 довольно стар, и в настоящее время вы стремитесь к С++ 20, если только вы не можете этого сделать из-за соображений обратной совместимости.
- Вы определенно использовали бы шаблоны, чтобы сделать свой код более универсальным. В частности, вы можете создать
template<typename T> class BSTиметь отдельныйclass Petи тогда вы можетеtypedef BST<Pet> ShelterBST. - Конечно, если вы просто хотите, чтобы контейнер для питомцев отсортировался по имени, и вас не слишком заботит базовая структура данных, вы должны использовать стандартный контейнер, например
std::setилиstd::map.
Тем не менее, вы обязательно узнаете что-то полезное о C++ из данного задания.
Формат документации
Я вижу, что в вашем коде есть комментарии, которые очень похожи на Комментарии в стиле Doxygen, но не совсем. Было бы здорово, если бы вы точно следовали формату Doyxgen; таким образом, инструменты Doxygen могут генерировать версии документации в формате PDF и HTML, проверять, что вы все задокументировали, а некоторые редакторы кода даже понимают Doxygen и смогут отображать его при наведении курсора на имена функций и классов.
Отдельные проблемы
Ваш код не соответствует разделение интересов принцип конструкции. ShelterBST class делает слишком много вещей: он содержит логику управления BST, имеет встроенные модульные тесты и может взаимодействовать с пользователем, выводя данные напрямую в std::cout.
Наличие функции void insertPet(std::string name, int age) это хорошо, но void searchPet(std::string name) плохо, потому что ничего не возвращает вызывающей стороне, а просто выводит std::cout. Это означает, что вы не можете делать ничего, кроме вывода, независимо от того, находится ли это домашнее животное в базе данных или нет. Что, если вы хотите узнать его возраст? Что, если бы в памяти хранилось больше данных? class Pet? В идеале такая функция, как searchPet() вернул бы указатель на Pet что было найдено или nullptr на случай, если животное не будет найдено. Затем вызывающая сторона может решить, что делать с результатом, что значительно увеличивает гибкость этой функции.
Итак, как это сделать для таких вещей, как inOrderDisplay()? Вы хотите отделить процесс обхода BST от отображения элементов. Вы можете сделать это в С++, создав функцию inOrderVisit() это занимает std::function в качестве параметра, поэтому вызывающая сторона может передать функцию inOrderVisit()а последний может вызывать его для каждого элемента, который он посещает:
void inOrderVisit(std::function<void(Pet*)>);
Затем вызывающий абонент может сделать:
ShelterBST shelter;
…
auto printPet = [](Pet* pet) {
std::cout << "Name: [" << pet->name << "] Age: [" << pet->age << "]\n";
};
shelter.inOrderVisit(printPet);
Обратите внимание, что printPet в этом примере является лямбдой (по-прежнему С++ 11), но вы также можете просто сделать ее обычной функцией. Вы хотите написать тест для обхода предварительного заказа? Больше нет необходимости в preOrderHelper(); просто пройти preOrderVisit() другая функция:
ShelterBST shelter;
shelter.addPet("F", 1");
shelter.addPet("C", 2");
…
std::vector<std::string> names;
shelter.preOrderVisit([&](Pet *pet){ names.push_back(pet->name); });
Более продвинутым вариантом было бы добавление итераторов, поэтому вы можете использовать диапазон значений.for петля. Это позволит вам написать такой код:
ShelterBST shelter;
…
for (Pet* pet: shelter.preOrder()) {
std::cout << "Name: [" << pet->name << "] Age: [" << pet->age << "]\n";
}
Модульные тесты
Для модульных тестов в идеале вам нужно будет использовать общедоступные функции только для проверки функциональности вашего класса. С приведенными выше изменениями вы уже можете проверить, работают ли функции поиска и обхода без необходимости доступа к закрытым функциям. Однако, если вы действительно хотите иметь доступ к закрытым членам в тестах, то один из способов — создать тестовый класс или функцию и объявить это как friend внутри ShelterBST. Таким образом, тестовый класс имеет доступ к закрытым внутренним компонентам любого ShelterBST объект.
Есть множество библиотек модульных тестов для C++. Обычно вы используете один из тех, который соответствует вашим потребностям. Более мощные библиотеки упрощают как написание тестов, так и их выполнение.
Как насчет конструктора перемещения?
Я вижу, вы написали конструктор копирования и оператор присваивания копирования, но как насчет конструктора перемещения и оператора присваивания перемещения? Было бы неплохо реализовать и их.
Использовать '\n' вместо std::endl
Предпочитаю использовать '\n' вместо std::endl; последний эквивалентен первому, но также приводит к сбросу вывода, что обычно не требуется и отрицательно влияет на производительность.
Избегайте устаревших и нестандартных функций
usleep() не является ни C, ни C++. Это функция POSIX, которая устарела в 2001 году. Если вы пишете на C, используйте функцию POSIX. nanosleep() или clock_nanosleep() функции. Однако в С++ 11 стандартный способ заснуть — использовать std::this_thread::sleep_for().
Г. Спал
