Problem

Design and implement a circular stack. A circular stack is similar to a regular stack, but the underlying array is used in a circular manner. The operations pushpoptop (peek), and isEmpty should be supported.

Examples

Example 1:

Input: 
Operations: ["push(1)", "push(2)", "top()", "pop()", "isEmpty()"]
Output: 
[null, null, 2, 2, false]
Explanation: 
- push(1) adds 1 to the stack.
- push(2) adds 2 to the stack.
- top() returns the current top element, which is 2.
- pop() removes and returns the current top element, which is 2.
- isEmpty() returns false as the stack is not empty.

Solution

Method 1 - Using array

A stack follows the Last In First Out (LIFO) principle. By using a circular array to implement a stack, we can efficiently manage the stack operations by utilizing a circular buffer.

Here is the approach:

  1. Use an array to store the elements.
  2. Use an integer top to keep track of the index of the most recently added element.
  3. If top reaches the end of the array, it wraps around to the beginning (circular logic).

Here are the operations:

  1. Circular Array: The array is considered circular, meaning that if the end of the array is reached, it wraps around to the beginning.
  2. Push Operation: Inserts an element at the next available position considering the circular nature of the array.
  3. Pop Operation: Removes the most recently added element considering the circular nature of the array.
  4. Top Operation: Retrieves the most recently added element without removing it.
  5. isEmpty Operation: Checks if the stack is empty.
  6. isFull Operation: Checks if the stack is full.
  7. Static Capacity: The stack has a fixed capacity and will not resize.

Code

Java
public class Solution {
    private int[] stack;
    private int top;
    private int capacity;
    private int size;

    public Solution(int capacity) {
        this.capacity = capacity;
        stack = new int[capacity];
        top = -1;
        size = 0;
    }

    public void push(int x) {
        if (isFull()) {
            throw new IllegalStateException("Stack overflow");
        }
        top = (top + 1) % capacity;
        stack[top] = x;
        size++;
    }

    public int pop() {
        if (isEmpty()) {
            throw new IllegalStateException("Stack underflow");
        }
        int value = stack[top];
        stack[top] = 0; // Clear element
        top = (top - 1 + capacity) % capacity;
        size--;
        return value;
    }

    public int top() {
        if (isEmpty()) {
            throw new IllegalStateException("Stack is empty");
        }
        return stack[top];
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public boolean isFull() {
        return size == capacity;
    }

    public static void main(String[] args) {
        Solution stack = new Solution(2);
        stack.push(1);
        stack.push(2);
        System.out.println(stack.top());     // Outputs: 2
        System.out.println(stack.pop());     // Outputs: 2
        System.out.println(stack.isEmpty()); // Outputs: false
        stack.push(3);
        System.out.println(stack.pop());     // Outputs: 3
        System.out.println(stack.isEmpty()); // Outputs: false
        System.out.println(stack.pop());     // Outputs: 1
        System.out.println(stack.isEmpty()); // Outputs: true
    }
}
Python
class Solution:
    def __init__(self, capacity: int) -> None:
        self.stack: List[int] = [0] * capacity
        self.top: int = -1
        self.capacity: int = capacity
        self.size: int = 0

    def push(self, x: int) -> None:
        if self.is_full():
            raise Exception("Stack overflow")
        self.top = (self.top + 1) % self.capacity
        self.stack[self.top] = x
        self.size += 1

    def pop(self) -> int:
        if self.is_empty():
            raise Exception("Stack underflow")
        value = self.stack[self.top]
        self.stack[self.top] = 0
        self.top = (self.top - 1 + self.capacity) % self.capacity
        self.size -= 1
        return value

    def top(self) -> int:
        if self.is_empty():
            raise Exception("Stack is empty")
        return self.stack[self.top]

    def is_empty(self) -> bool:
        return self.size == 0

    def is_full(self) -> bool:
        return self.size == self.capacity

# Example usage
stack = Solution(2)
stack.push(1)
stack.push(2)
print(stack.top())     # Outputs: 2
print(stack.pop())     # Outputs: 2
print(stack.is_empty()) # Outputs: False
stack.push(3)
print(stack.pop())     # Outputs: 3
print(stack.is_empty()) # Outputs: False
print(stack.pop())     # Outputs: 1
print(stack.is_empty()) # Outputs: True

Complexity

  • ⏰ Time complexity
    • pushO(1)
    • popO(1)
    • topO(1)
    • isEmptyO(1)
    • isFullO(1)
  • 🧺 Space complexity: O(n), where n is the fixed capacity of the stack.