diff --git a/DIRECTORY.md b/DIRECTORY.md index ad893d45f..061cf93a4 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -866,6 +866,7 @@ * [QueueByTwoStacksTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/queues/QueueByTwoStacksTest.java) * [QueueTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/queues/QueueTest.java) * stacks + * [NodeStackTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/stacks/NodeStackTest.java) * [StackArrayListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/stacks/StackArrayListTest.java) * [StackArrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/stacks/StackArrayTest.java) * [StackOfLinkedListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/stacks/StackOfLinkedListTest.java) diff --git a/src/main/java/com/thealgorithms/datastructures/stacks/NodeStack.java b/src/main/java/com/thealgorithms/datastructures/stacks/NodeStack.java index 7c4f334cd..384cf3c03 100644 --- a/src/main/java/com/thealgorithms/datastructures/stacks/NodeStack.java +++ b/src/main/java/com/thealgorithms/datastructures/stacks/NodeStack.java @@ -1,161 +1,109 @@ package com.thealgorithms.datastructures.stacks; /** - * Implementation of a stack using nodes. Unlimited size, no arraylist. + * A stack implementation using linked nodes, supporting unlimited size without an ArrayList. * - * @author Kyler Smith, 2017 + *

Each node in the stack contains data of generic type {@code Item}, along with references + * to the next and previous nodes, supporting typical stack operations. + * + *

The stack follows a Last-In-First-Out (LIFO) order where elements added last are + * removed first. Supported operations include push, pop, and peek. + * + * @param the type of elements held in this stack */ public class NodeStack { /** - * Entry point for the program. + * Node class representing each element in the stack. */ - public static void main(String[] args) { - NodeStack stack = new NodeStack(); + private class Node { + Item data; + Node previous; - stack.push(3); - stack.push(4); - stack.push(5); - System.out.println("Testing :"); - stack.print(); // prints : 5 4 3 - - Integer x = stack.pop(); // x = 5 - stack.push(1); - stack.push(8); - Integer y = stack.peek(); // y = 8 - System.out.println("Testing :"); - stack.print(); // prints : 8 1 4 3 - - System.out.println("Testing :"); - System.out.println("x : " + x); - System.out.println("y : " + y); + Node(Item data) { + this.data = data; + this.previous = null; + } } - /** - * Information each node should contain. - * - * @value data : information of the value in the node - * @value head : the head of the stack - * @value next : the next value from this node - * @value previous : the last value from this node - * @value size : size of the stack - */ - private Item data; - - private static NodeStack head; - private NodeStack previous; - private static int size = 0; + private Node head; // Top node in the stack + private int size; // Number of elements in the stack /** - * Constructors for the NodeStack. + * Constructs an empty NodeStack. */ public NodeStack() { - } - - private NodeStack(Item item) { - this.data = item; + head = null; + size = 0; } /** - * Put a value onto the stack. + * Pushes an item onto the stack. * - * @param item : value to be put on the stack. + * @param item the item to be pushed onto the stack */ public void push(Item item) { - NodeStack newNs = new NodeStack(item); - - if (this.isEmpty()) { - NodeStack.setHead(new NodeStack<>(item)); - newNs.setNext(null); - newNs.setPrevious(null); - } else { - newNs.setPrevious(NodeStack.head); - NodeStack.head.setNext(newNs); - NodeStack.setHead(newNs); - } - - NodeStack.setSize(NodeStack.getSize() + 1); + Node newNode = new Node(item); + newNode.previous = head; + head = newNode; + size++; } /** - * Value to be taken off the stack. + * Removes and returns the item at the top of the stack. * - * @return item : value that is returned. + * @return the item at the top of the stack, or {@code null} if the stack is empty + * @throws IllegalStateException if the stack is empty */ public Item pop() { - Item item = (Item) NodeStack.head.getData(); - - NodeStack.setHead(NodeStack.head.getPrevious()); - NodeStack.head.setNext(null); - - NodeStack.setSize(NodeStack.getSize() - 1); - - return item; + if (isEmpty()) { + throw new IllegalStateException("Cannot pop from an empty stack."); + } + Item data = head.data; + head = head.previous; + size--; + return data; } /** - * Value that is next to be taken off the stack. + * Returns the item at the top of the stack without removing it. * - * @return item : the next value that would be popped off the stack. + * @return the item at the top of the stack, or {@code null} if the stack is empty + * @throws IllegalStateException if the stack is empty */ public Item peek() { - return (Item) NodeStack.head.getData(); + if (isEmpty()) { + throw new IllegalStateException("Cannot peek from an empty stack."); + } + return head.data; } /** - * If the stack is empty or there is a value in. + * Checks whether the stack is empty. * - * @return boolean : whether or not the stack has anything in it. + * @return {@code true} if the stack has no elements, {@code false} otherwise */ public boolean isEmpty() { - return NodeStack.getSize() == 0; + return head == null; } /** - * Returns the size of the stack. + * Returns the number of elements currently in the stack. * - * @return int : number of values in the stack. + * @return the size of the stack */ public int size() { - return NodeStack.getSize(); - } - - /** - * Print the contents of the stack in the following format. - * - *

- * x <- head (next out) y z <- tail (first in) . . . - */ - public void print() { - for (NodeStack n = NodeStack.head; n != null; n = n.previous) { - System.out.println(n.getData().toString()); - } - } - - private static void setHead(NodeStack ns) { - NodeStack.head = ns; - } - - private void setNext(NodeStack next) { - } - - private NodeStack getPrevious() { - return previous; - } - - private void setPrevious(NodeStack previous) { - this.previous = previous; - } - - private static int getSize() { return size; } - private static void setSize(int size) { - NodeStack.size = size; - } - - private Item getData() { - return this.data; + /** + * Prints the contents of the stack from top to bottom. + */ + public void print() { + Node current = head; + while (current != null) { + System.out.println(current.data); + current = current.previous; + } } } diff --git a/src/test/java/com/thealgorithms/datastructures/stacks/NodeStackTest.java b/src/test/java/com/thealgorithms/datastructures/stacks/NodeStackTest.java new file mode 100644 index 000000000..e05319359 --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/stacks/NodeStackTest.java @@ -0,0 +1,85 @@ +package com.thealgorithms.datastructures.stacks; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class NodeStackTest { + + @Test + void testPush() { + NodeStack stack = new NodeStack<>(); + stack.push(10); + stack.push(20); + assertEquals(20, stack.peek(), "Top element should be 20 after pushing 10 and 20."); + } + + @Test + void testPop() { + NodeStack stack = new NodeStack<>(); + stack.push("First"); + stack.push("Second"); + assertEquals("Second", stack.pop(), "Pop should return 'Second', the last pushed element."); + assertEquals("First", stack.pop(), "Pop should return 'First' after 'Second' is removed."); + } + + @Test + void testPopOnEmptyStack() { + NodeStack stack = new NodeStack<>(); + assertThrows(IllegalStateException.class, stack::pop, "Popping an empty stack should throw IllegalStateException."); + } + + @Test + void testPeek() { + NodeStack stack = new NodeStack<>(); + stack.push(5); + stack.push(15); + assertEquals(15, stack.peek(), "Peek should return 15, the top element."); + stack.pop(); + assertEquals(5, stack.peek(), "Peek should return 5 after 15 is popped."); + } + + @Test + void testPeekOnEmptyStack() { + NodeStack stack = new NodeStack<>(); + assertThrows(IllegalStateException.class, stack::peek, "Peeking an empty stack should throw IllegalStateException."); + } + + @Test + void testIsEmpty() { + NodeStack stack = new NodeStack<>(); + assertTrue(stack.isEmpty(), "Newly initialized stack should be empty."); + stack.push('A'); + assertFalse(stack.isEmpty(), "Stack should not be empty after a push operation."); + stack.pop(); + assertTrue(stack.isEmpty(), "Stack should be empty after popping the only element."); + } + + @Test + void testSize() { + NodeStack stack = new NodeStack<>(); + assertEquals(0, stack.size(), "Size of empty stack should be 0."); + stack.push(3); + stack.push(6); + assertEquals(2, stack.size(), "Size should be 2 after pushing two elements."); + stack.pop(); + assertEquals(1, stack.size(), "Size should be 1 after popping one element."); + stack.pop(); + assertEquals(0, stack.size(), "Size should be 0 after popping all elements."); + } + + @Test + void testPrint() { + NodeStack stack = new NodeStack<>(); + stack.push(1); + stack.push(2); + stack.push(3); + + // Output verification would ideally be handled through a different means + // but you can print as a basic check to confirm method runs without errors. + stack.print(); + } +}