Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
package com.thealgorithms.datastructures.stacks;

/**
* This class implements a Stack using a regular array.
* Implements a generic stack using an array.
*
* <p>This stack automatically resizes when necessary, growing to accommodate additional elements and
* shrinking to conserve memory when its size significantly decreases.
*
* <p>Elements are pushed and popped in LIFO (last-in, first-out) order, where the last element added
* is the first to be removed.
*
* @param <T> the type of elements in this stack
*/
Expand All @@ -13,11 +19,20 @@ public class StackArray<T> implements Stack<T> {
private T[] stackArray;
private int top;

/**
* Creates a stack with a default capacity.
*/
@SuppressWarnings("unchecked")
public StackArray() {
this(DEFAULT_CAPACITY);
}

/**
* Creates a stack with a specified initial capacity.
*
* @param size the initial capacity of the stack, must be greater than 0
* @throws IllegalArgumentException if size is less than or equal to 0
*/
@SuppressWarnings("unchecked")
public StackArray(int size) {
if (size <= 0) {
Expand All @@ -28,6 +43,11 @@ public StackArray(int size) {
this.top = -1;
}

/**
* Pushes an element onto the top of the stack. Resizes the stack if it is full.
*
* @param value the element to push
*/
@Override
public void push(T value) {
if (isFull()) {
Expand All @@ -36,6 +56,13 @@ public void push(T value) {
stackArray[++top] = value;
}

/**
* Removes and returns the element from the top of the stack. Shrinks the stack if
* its size is below a quarter of its capacity, but not below the default capacity.
*
* @return the element removed from the top of the stack
* @throws IllegalStateException if the stack is empty
*/
@Override
public T pop() {
if (isEmpty()) {
Expand All @@ -48,6 +75,12 @@ public T pop() {
return value;
}

/**
* Returns the element at the top of the stack without removing it.
*
* @return the top element of the stack
* @throws IllegalStateException if the stack is empty
*/
@Override
public T peek() {
if (isEmpty()) {
Expand All @@ -56,28 +89,72 @@ public T peek() {
return stackArray[top];
}

/**
* Resizes the internal array to a new capacity.
*
* @param newSize the new size of the stack array
*/
private void resize(int newSize) {
@SuppressWarnings("unchecked") T[] newArray = (T[]) new Object[newSize];
System.arraycopy(stackArray, 0, newArray, 0, top + 1);
stackArray = newArray;
maxSize = newSize;
}

/**
* Checks if the stack is full.
*
* @return true if the stack is full, false otherwise
*/
public boolean isFull() {
return top + 1 == maxSize;
}

/**
* Checks if the stack is empty.
*
* @return true if the stack is empty, false otherwise
*/
@Override
public boolean isEmpty() {
return top == -1;
}

@Override public void makeEmpty() { // Doesn't delete elements in the array but if you call
top = -1; // push method after calling makeEmpty it will overwrite previous values
/**
* Empties the stack, marking it as empty without deleting elements. Elements are
* overwritten on subsequent pushes.
*/
@Override
public void makeEmpty() {
top = -1;
}

/**
* Returns the number of elements currently in the stack.
*
* @return the size of the stack
*/
@Override
public int size() {
return top + 1;
}

/**
* Returns a string representation of the stack.
*
* @return a string representation of the stack
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("StackArray [");
for (int i = 0; i <= top; i++) {
sb.append(stackArray[i]);
if (i < top) {
sb.append(", ");
}
}
sb.append("]");
return sb.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ void testPushAndPop() {
stack.push(4);
stack.push(5);

Assertions.assertEquals(5, stack.pop()); // Stack follows LIFO, so 5 should be popped first
Assertions.assertEquals(4, stack.pop()); // Next, 4 should be popped
Assertions.assertEquals(3, stack.pop()); // Followed by 3
Assertions.assertEquals(2, stack.pop()); // Then 2
Assertions.assertEquals(1, stack.pop()); // Finally 1
Assertions.assertEquals(5, stack.pop());
Assertions.assertEquals(4, stack.pop());
Assertions.assertEquals(3, stack.pop());
Assertions.assertEquals(2, stack.pop());
Assertions.assertEquals(1, stack.pop());
}

@Test
Expand All @@ -34,34 +34,34 @@ void testPeek() {
stack.push(20);
stack.push(30);

Assertions.assertEquals(30, stack.peek()); // Peek should return 30, the top of the stack
Assertions.assertEquals(3, stack.size()); // Size should remain 3 after peek
Assertions.assertEquals(30, stack.peek());
Assertions.assertEquals(3, stack.size());

stack.pop();
Assertions.assertEquals(20, stack.peek()); // After popping, peek should return 20
Assertions.assertEquals(20, stack.peek());
}

@Test
void testIsEmpty() {
Assertions.assertTrue(stack.isEmpty()); // Initially, the stack should be empty
Assertions.assertTrue(stack.isEmpty());
stack.push(42);
Assertions.assertFalse(stack.isEmpty()); // After pushing an element, the stack should not be empty
Assertions.assertFalse(stack.isEmpty());
stack.pop();
Assertions.assertTrue(stack.isEmpty()); // After popping the only element, the stack should be empty again
Assertions.assertTrue(stack.isEmpty());
}

@Test
void testResizeOnPush() {
StackArray<Integer> smallStack = new StackArray<>(2); // Start with a small stack size
StackArray<Integer> smallStack = new StackArray<>(2);
smallStack.push(1);
smallStack.push(2);
Assertions.assertTrue(smallStack.isFull()); // Initially, the stack should be full
Assertions.assertTrue(smallStack.isFull());

smallStack.push(3); // This push should trigger a resize
Assertions.assertFalse(smallStack.isFull()); // The stack should no longer be full after resize
Assertions.assertEquals(3, smallStack.size()); // Size should be 3 after pushing 3 elements
smallStack.push(3);
Assertions.assertFalse(smallStack.isFull());
Assertions.assertEquals(3, smallStack.size());

Assertions.assertEquals(3, smallStack.pop()); // LIFO behavior check
Assertions.assertEquals(3, smallStack.pop());
Assertions.assertEquals(2, smallStack.pop());
Assertions.assertEquals(1, smallStack.pop());
}
Expand All @@ -74,13 +74,13 @@ void testResizeOnPop() {
stack.push(3);
stack.push(4);

stack.pop(); // Removing elements should trigger a resize when less than 1/4 of the stack is used
stack.pop();
stack.pop();
Assertions.assertEquals(1, stack.size()); // After popping, only one element should remain
stack.pop();
Assertions.assertEquals(1, stack.size());

stack.pop();
Assertions.assertTrue(stack.isEmpty()); // The stack should be empty now
Assertions.assertTrue(stack.isEmpty());
}

@Test
Expand All @@ -90,32 +90,40 @@ void testMakeEmpty() {
stack.push(3);
stack.makeEmpty();

Assertions.assertTrue(stack.isEmpty()); // The stack should be empty after calling makeEmpty
Assertions.assertThrows(IllegalStateException.class, stack::pop); // Popping from empty stack should throw exception
Assertions.assertTrue(stack.isEmpty());
Assertions.assertThrows(IllegalStateException.class, stack::pop);
}

@Test
void testPopEmptyStackThrowsException() {
Assertions.assertThrows(IllegalStateException.class, stack::pop); // Popping from an empty stack should throw an exception
Assertions.assertThrows(IllegalStateException.class, stack::pop);
}

@Test
void testPeekEmptyStackThrowsException() {
Assertions.assertThrows(IllegalStateException.class, stack::peek); // Peeking into an empty stack should throw an exception
Assertions.assertThrows(IllegalStateException.class, stack::peek);
}

@Test
void testConstructorWithInvalidSizeThrowsException() {
Assertions.assertThrows(IllegalArgumentException.class, () -> new StackArray<>(0)); // Size 0 is invalid
Assertions.assertThrows(IllegalArgumentException.class, () -> new StackArray<>(-5)); // Negative size is invalid
Assertions.assertThrows(IllegalArgumentException.class, () -> new StackArray<>(0));
Assertions.assertThrows(IllegalArgumentException.class, () -> new StackArray<>(-5));
}

@Test
void testDefaultConstructor() {
StackArray<Integer> defaultStack = new StackArray<>(); // Using default constructor
Assertions.assertEquals(0, defaultStack.size()); // Initially, size should be 0
StackArray<Integer> defaultStack = new StackArray<>();
Assertions.assertEquals(0, defaultStack.size());

defaultStack.push(1);
Assertions.assertEquals(1, defaultStack.size()); // After pushing, size should be 1
Assertions.assertEquals(1, defaultStack.size());
}

@Test
void testToString() {
stack.push(1);
stack.push(2);
stack.push(3);
Assertions.assertEquals("StackArray [1, 2, 3]", stack.toString());
}
}