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();
+ }
+}