diff --git a/DIRECTORY.md b/DIRECTORY.md index c613222ef..3a0b61323 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -66,7 +66,7 @@ * [Reverse A Linked List](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/data_structures/reverse_a_linked_list.cpp) * [Skip List](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/data_structures/skip_list.cpp) * [Sparse Table](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/data_structures/sparse_table.cpp) - * [Stack](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/data_structures/stack.h) + * [Stack](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/data_structures/stack.hpp) * [Stack Using Array](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/data_structures/stack_using_array.cpp) * [Stack Using Linked List](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/data_structures/stack_using_linked_list.cpp) * [Stack Using Queue](https://github.com/TheAlgorithms/C-Plus-Plus/blob/HEAD/data_structures/stack_using_queue.cpp) diff --git a/data_structures/stack.h b/data_structures/stack.h deleted file mode 100644 index 483648046..000000000 --- a/data_structures/stack.h +++ /dev/null @@ -1,150 +0,0 @@ -/** - * @file stack.h - * @author danghai - * @brief This class specifies the basic operation on a stack as a linked list - **/ -#ifndef DATA_STRUCTURES_STACK_H_ -#define DATA_STRUCTURES_STACK_H_ - -#include -#include - -/** Definition of the node as a linked-list - * \tparam Type type of data nodes of the linked list should contain - */ -template -struct node { - Type data; ///< data at current node - node *next; ///< pointer to the next ::node instance -}; - -/** Definition of the stack class - * \tparam Type type of data nodes of the linked list in the stack should - * contain - */ -template -class stack { - public: - /** Show stack */ - void display() { - node *current = stackTop; - std::cout << "Top --> "; - while (current != nullptr) { - std::cout << current->data << " "; - current = current->next; - } - std::cout << std::endl; - std::cout << "Size of stack: " << size << std::endl; - } - - /** Default constructor*/ - stack() { - stackTop = nullptr; - size = 0; - } - - /** Copy constructor*/ - explicit stack(const stack &otherStack) { - node *newNode, *current, *last; - - /* If stack is no empty, make it empty */ - if (stackTop != nullptr) { - stackTop = nullptr; - } - if (otherStack.stackTop == nullptr) { - stackTop = nullptr; - } else { - current = otherStack.stackTop; - stackTop = new node; - stackTop->data = current->data; - stackTop->next = nullptr; - last = stackTop; - current = current->next; - /* Copy the remaining stack */ - while (current != nullptr) { - newNode = new node; - newNode->data = current->data; - newNode->next = nullptr; - last->next = newNode; - last = newNode; - current = current->next; - } - } - size = otherStack.size; - } - - /** Destructor */ - ~stack() {} - - /** Determine whether the stack is empty */ - bool isEmptyStack() { return (stackTop == nullptr); } - - /** Add new item to the stack */ - void push(Type item) { - node *newNode; - newNode = new node; - newNode->data = item; - newNode->next = stackTop; - stackTop = newNode; - size++; - } - - /** Return the top element of the stack */ - Type top() { - assert(stackTop != nullptr); - return stackTop->data; - } - - /** Remove the top element of the stack */ - void pop() { - node *temp; - if (!isEmptyStack()) { - temp = stackTop; - stackTop = stackTop->next; - delete temp; - size--; - } else { - std::cout << "Stack is empty !" << std::endl; - } - } - - /** Clear stack */ - void clear() { stackTop = nullptr; } - - /** Overload "=" the assignment operator */ - stack &operator=(const stack &otherStack) { - node *newNode, *current, *last; - - /* If stack is no empty, make it empty */ - if (stackTop != nullptr) { - stackTop = nullptr; - } - if (otherStack.stackTop == nullptr) { - stackTop = nullptr; - } else { - current = otherStack.stackTop; - stackTop = new node; - stackTop->data = current->data; - stackTop->next = nullptr; - last = stackTop; - current = current->next; - /* Copy the remaining stack */ - while (current != nullptr) { - newNode = new node; - newNode->data = current->data; - newNode->next = nullptr; - last->next = newNode; - last = newNode; - current = current->next; - } - } - size = otherStack.size; - return *this; - } - - private: - node *stackTop; /**< Pointer to the stack */ - int size; ///< size of stack -}; - -#endif // DATA_STRUCTURES_STACK_H_ diff --git a/data_structures/stack.hpp b/data_structures/stack.hpp new file mode 100644 index 000000000..419a07c67 --- /dev/null +++ b/data_structures/stack.hpp @@ -0,0 +1,106 @@ +/** + * @file + * @author danghai + * @author [Piotr Idzik](https://github.com/vil02) + * @brief This class specifies the basic operation on a stack as a linked list + **/ +#ifndef DATA_STRUCTURES_STACK_HPP_ +#define DATA_STRUCTURES_STACK_HPP_ + +#include /// for IO operations +#include /// for std::shared_ptr +#include /// for std::invalid_argument +#include /// for std::vector + +/** Definition of the node as a linked-list + * \tparam ValueType type of data nodes of the linked list should contain + */ +template +struct node { + ValueType data = {}; ///< data at current node + std::shared_ptr> next = + {}; ///< pointer to the next ::node instance +}; + +template +void traverse(const Node* const inNode, const Action& action) { + if (inNode) { + action(*inNode); + traverse(inNode->next.get(), action); + } +} + +/** Definition of the stack class + * \tparam value_type type of data nodes of the linked list in the stack should + * contain + */ +template +class stack { + public: + using value_type = ValueType; + + /** Show stack */ + void display() const { + std::cout << "Top --> "; + traverse(stackTop.get(), [](const node& inNode) { + std::cout << inNode.data << " "; + }); + std::cout << std::endl; + std::cout << "Size of stack: " << size << std::endl; + } + + std::vector toVector() const { + std::vector res; + res.reserve(this->size); + traverse(stackTop.get(), [&res](const node& inNode) { + res.push_back(inNode.data); + }); + return res; + } + + private: + void ensureNotEmpty() const { + if (isEmptyStack()) { + throw std::invalid_argument("Stack is empty."); + } + } + + public: + /** Determine whether the stack is empty */ + bool isEmptyStack() const { return (stackTop == nullptr); } + + /** Add new item to the stack */ + void push(const value_type& item) { + auto newNode = std::make_shared>(); + newNode->data = item; + newNode->next = stackTop; + stackTop = newNode; + size++; + } + + /** Return the top element of the stack */ + value_type top() const { + ensureNotEmpty(); + return stackTop->data; + } + + /** Remove the top element of the stack */ + void pop() { + ensureNotEmpty(); + stackTop = stackTop->next; + size--; + } + + /** Clear stack */ + void clear() { + stackTop = nullptr; + size = 0; + } + + private: + std::shared_ptr> stackTop = + {}; /**< Pointer to the stack */ + std::size_t size = 0; ///< size of stack +}; + +#endif // DATA_STRUCTURES_STACK_HPP_ diff --git a/data_structures/test_stack.cpp b/data_structures/test_stack.cpp index aa636916f..dfff0b50e 100644 --- a/data_structures/test_stack.cpp +++ b/data_structures/test_stack.cpp @@ -1,59 +1,203 @@ -#include +#include /// for assert +#include /// for std::cout +#include /// std::invalid_argument +#include /// for std::vector -#include "./stack.h" +#include "./stack.hpp" + +template +void testConstructedStackIsEmpty() { + const stack curStack; + assert(curStack.isEmptyStack()); +} + +void testPush() { + using valueType = int; + stack curStack; + curStack.push(10); + curStack.push(20); + curStack.push(30); + curStack.push(40); + const auto expectedData = std::vector({40, 30, 20, 10}); + assert(curStack.toVector() == expectedData); +} + +void testTop() { + using valueType = unsigned; + stack curStack; + curStack.push(1); + curStack.push(2); + curStack.push(3); + curStack.push(4); + assert(curStack.top() == static_cast(4)); +} + +void testPop() { + using valueType = int; + stack curStack; + curStack.push(100); + curStack.push(200); + curStack.push(300); + + assert(curStack.top() == static_cast(300)); + curStack.pop(); + assert(curStack.top() == static_cast(200)); + curStack.pop(); + assert(curStack.top() == static_cast(100)); + curStack.pop(); + assert(curStack.isEmptyStack()); +} + +void testClear() { + stack curStack; + curStack.push(1000); + curStack.push(2000); + curStack.clear(); + assert(curStack.isEmptyStack()); +} + +void testCopyOfStackHasSameData() { + stack stackA; + stackA.push(10); + stackA.push(200); + stackA.push(3000); + const auto stackB(stackA); + assert(stackA.toVector() == stackB.toVector()); +} + +void testPushingToCopyDoesNotChangeOriginal() { + using valueType = int; + stack stackA; + stackA.push(10); + stackA.push(20); + stackA.push(30); + auto stackB(stackA); + stackB.push(40); + + const auto expectedDataA = std::vector({30, 20, 10}); + const auto expectedDataB = std::vector({40, 30, 20, 10}); + + assert(stackA.toVector() == expectedDataA); + assert(stackB.toVector() == expectedDataB); +} + +void testPoppingFromCopyDoesNotChangeOriginal() { + using valueType = int; + stack stackA; + stackA.push(10); + stackA.push(20); + stackA.push(30); + auto stackB(stackA); + stackB.pop(); + + const auto expectedDataA = std::vector({30, 20, 10}); + const auto expectedDataB = std::vector({20, 10}); + + assert(stackA.toVector() == expectedDataA); + assert(stackB.toVector() == expectedDataB); +} + +void testPushingToOrginalDoesNotChangeCopy() { + using valueType = int; + stack stackA; + stackA.push(10); + stackA.push(20); + stackA.push(30); + const auto stackB(stackA); + stackA.push(40); + + const auto expectedDataA = std::vector({40, 30, 20, 10}); + const auto expectedDataB = std::vector({30, 20, 10}); + + assert(stackA.toVector() == expectedDataA); + assert(stackB.toVector() == expectedDataB); +} + +void testPoppingFromOrginalDoesNotChangeCopy() { + using valueType = int; + stack stackA; + stackA.push(10); + stackA.push(20); + stackA.push(30); + const auto stackB(stackA); + stackA.pop(); + + const auto expectedDataA = std::vector({20, 10}); + const auto expectedDataB = std::vector({30, 20, 10}); + + assert(stackA.toVector() == expectedDataA); + assert(stackB.toVector() == expectedDataB); +} + +void testAssign() { + using valueType = int; + stack stackA; + stackA.push(10); + stackA.push(20); + stackA.push(30); + stack stackB = stackA; + stackA.pop(); + stackB.push(40); + + const auto expectedDataA = std::vector({20, 10}); + const auto expectedDataB = std::vector({40, 30, 20, 10}); + + assert(stackA.toVector() == expectedDataA); + assert(stackB.toVector() == expectedDataB); + + stackB = stackA; + stackA.pop(); + stackB.push(5); + stackB.push(6); + + const auto otherExpectedDataA = std::vector({10}); + const auto otherExpectedDataB = std::vector({6, 5, 20, 10}); + + assert(stackA.toVector() == otherExpectedDataA); + assert(stackB.toVector() == otherExpectedDataB); +} + +void testTopThrowsAnvalidArgumentWhenStackEmpty() { + const stack curStack; + bool wasException = false; + try { + curStack.top(); + } catch (const std::invalid_argument&) { + wasException = true; + } + assert(wasException); +} + +void testPopThrowsAnvalidArgumentWhenStackEmpty() { + stack curStack; + bool wasException = false; + try { + curStack.pop(); + } catch (const std::invalid_argument&) { + wasException = true; + } + assert(wasException); +} int main() { - stack stk; - std::cout << "---------------------- Test construct ----------------------" - << std::endl; - stk.display(); - std::cout - << "---------------------- Test isEmptyStack ----------------------" - << std::endl; - if (stk.isEmptyStack()) - std::cout << "PASS" << std::endl; - else - std::cout << "FAIL" << std::endl; - std::cout << "---------------------- Test push ----------------------" - << std::endl; - std::cout << "After pushing 10 20 30 40 into stack: " << std::endl; - stk.push(10); - stk.push(20); - stk.push(30); - stk.push(40); - stk.display(); - std::cout << "---------------------- Test top ----------------------" - << std::endl; - int value = stk.top(); - if (value == 40) - std::cout << "PASS" << std::endl; - else - std::cout << "FAIL" << std::endl; - std::cout << "---------------------- Test pop ----------------------" - << std::endl; - stk.display(); - stk.pop(); - stk.pop(); - std::cout << "After popping 2 times: " << std::endl; - stk.display(); - std::cout << "---------------------- Test overload = operator " - "----------------------" - << std::endl; - stack stk1; - std::cout << "stk current: " << std::endl; - stk.display(); - std::cout << std::endl << "Assign stk1 = stk " << std::endl; - stk1 = stk; - stk1.display(); - std::cout << std::endl << "After pushing 8 9 10 into stk1:" << std::endl; - stk1.push(8); - stk1.push(9); - stk1.push(10); - stk1.display(); - std::cout << std::endl << "stk current: " << std::endl; - stk.display(); - std::cout << "Assign back stk = stk1:" << std::endl; - stk = stk1; - stk.display(); + testConstructedStackIsEmpty(); + testConstructedStackIsEmpty(); + + testPush(); + testPop(); + testClear(); + + testCopyOfStackHasSameData(); + testPushingToCopyDoesNotChangeOriginal(); + testPoppingFromCopyDoesNotChangeOriginal(); + testPushingToOrginalDoesNotChangeCopy(); + testPoppingFromOrginalDoesNotChangeCopy(); + + testAssign(); + + testTopThrowsAnvalidArgumentWhenStackEmpty(); + testPopThrowsAnvalidArgumentWhenStackEmpty(); + + std::cout << "All tests pass!\n"; return 0; } diff --git a/data_structures/test_stack_students.cpp b/data_structures/test_stack_students.cpp index a6048c4c9..dc3514bcb 100644 --- a/data_structures/test_stack_students.cpp +++ b/data_structures/test_stack_students.cpp @@ -9,16 +9,17 @@ ************************************************************ * */ #include +#include #include #include #include #include -#include "./stack.h" +#include "./stack.hpp" int main(int argc, char* argv[]) { - double GPA; - double highestGPA; + double GPA = NAN; + double highestGPA = NAN; std::string name; assert(argc == 2);