Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Enhance docs, remove main, add tests in NodeStack #6017

Merged
merged 7 commits into from
Oct 26, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
refactor: Enhance docs, remove main, add tests in NodeStack
  • Loading branch information
Hardvan committed Oct 26, 2024
commit 27ef27b11e66c68133e96f5360388057263a0ee4
162 changes: 55 additions & 107 deletions src/main/java/com/thealgorithms/datastructures/stacks/NodeStack.java
Original file line number Diff line number Diff line change
@@ -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
* <p>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.
*
* <p>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 <Item> the type of elements held in this stack
*/
public class NodeStack<Item> {

/**
* Entry point for the program.
* Node class representing each element in the stack.
*/
public static void main(String[] args) {
NodeStack<Integer> stack = new NodeStack<Integer>();

stack.push(3);
stack.push(4);
stack.push(5);
System.out.println("Testing :");
stack.print(); // prints : 5 4 3
private class Node {
Item data;
Node previous;

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<Item> newNs = new NodeStack<Item>(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();
return size;
}

/**
* Print the contents of the stack in the following format.
*
* <p>
* x <- head (next out) y z <- tail (first in) . . .
* Prints the contents of the stack from top to bottom.
*/
public void print() {
for (NodeStack<?> n = NodeStack.head; n != null; n = n.previous) {
System.out.println(n.getData().toString());
Node current = head;
while (current != null) {
System.out.println(current.data);
current = current.previous;
}
}

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;
}
}
Original file line number Diff line number Diff line change
@@ -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<Integer> 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<String> 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<Double> stack = new NodeStack<>();
assertThrows(IllegalStateException.class, stack::pop, "Popping an empty stack should throw IllegalStateException.");
}

@Test
void testPeek() {
NodeStack<Integer> 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<String> stack = new NodeStack<>();
assertThrows(IllegalStateException.class, stack::peek, "Peeking an empty stack should throw IllegalStateException.");
}

@Test
void testIsEmpty() {
NodeStack<Character> 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<Integer> 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<Integer> 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();
}
}