1. What Is a Stack?
A Stack is a linear data structure that stores elements in a sequential order, but with one strict rule — you can only insert or remove elements from one end, called the top.
It is one of the simplest yet most powerful data structures in computer science. Its strength comes from its constraint. Because everything happens at one end, the behavior of a stack is completely predictable.
The formal definition: A Stack is a linear data structure that follows the Last In, First Out (LIFO) principle — the element inserted last is the first one to be removed.
Here is what a stack looks like visually:
When you push 60 onto this stack, it goes on top. When you pop, 60 comes out first. That is LIFO.
2. Real-World Analogies
Before writing a single line of code, it helps to see stacks in the real world. These analogies will make the concept permanently stick.
A Pile of Plates In a cafeteria, clean plates are stacked on top of each other. You always pick the top plate. You always place a new plate on top. You never reach into the middle. This is a stack.
Browser Back Button Every time you visit a new webpage, it gets pushed onto a history stack. When you press the Back button, the browser pops the most recent page off the stack and takes you there. The page you visited first is at the bottom — you only reach it after going back through everything else.
Undo Feature in Text Editors When you type in a document and press Ctrl+Z, the most recent action is undone first. That is because every action you perform is pushed onto a stack. Undo simply pops from that stack.
Call Stack in Programming When a function calls another function, the current function's state is pushed onto the call stack. When the inner function finishes, it is popped off and execution returns to the outer function. This is the literal stack your programs run on.
A Stack of Books Put five books on a table, one on top of another. You can only take the top book without knocking the pile over. That is a stack.
3. The LIFO Principle Explained
LIFO stands for Last In, First Out.
It means whatever you put in last is the first thing to come out. This is the exact opposite of a Queue (which is FIFO — First In, First Out).
Let us trace through an example step by step:
Every single operation happens only at the top. The bottom of the stack is never directly accessible.
4. Stack Operations & Time Complexity
A stack supports the following core operations:
| Operation | Description | Time Complexity |
| push(x) | Insert element x onto the top of the stack | O(1) |
| pop() | Remove and return the top element | O(1) |
| peek() / top() | Return the top element without removing it | O(1) |
| isEmpty() | Check if the stack has no elements | O(1) |
| isFull() | Check if the stack has reached its capacity (Array only) | O(1) |
| size() | Return the number of elements in the stack | O(1) |
| search(x) | Find position of element from top (Java built-in only) | O(n) |
All primary stack operations — push, pop, peek, isEmpty — run in O(1) constant time. This is what makes the stack so efficient. It does not matter whether the stack has 10 elements or 10 million — these operations are always instant.
Space complexity for a stack holding n elements is O(n).
5. Implementation 1 — Using a Static Array
This is the most fundamental way to implement a stack. We use a fixed-size array and a variable called top to track where the top of the stack currently is.
How it works:
topstarts at -1 (stack is empty)- On push: increment
top, then place the element atarr[top] - On pop: return
arr[top], then decrementtop - On peek: return
arr[top]without changing it
Key Points about Array Implementation:
- Fixed size — you must declare capacity upfront
- Very fast — direct array index access
- Stack Overflow is possible if capacity is exceeded
- Memory is pre-allocated even if stack is not full
6. Implementation 2 — Using an ArrayList
An ArrayList-based stack removes the fixed-size limitation. The ArrayList grows dynamically, so you never have to worry about stack overflow due to capacity.
How it works:
- The end of the ArrayList acts as the top
add()is used for pushremove(size - 1)is used for popget(size - 1)is used for peek
Key Points about ArrayList Implementation:
- Dynamic size — grows automatically as needed
- No overflow risk
- Slight overhead compared to raw array due to ArrayList internals
- Excellent for most practical use cases
7. Implementation 3 — Using a LinkedList
A LinkedList-based stack is the most memory-efficient approach when you do not know the stack size in advance. Each element (node) holds data and a pointer to the next node. The head of the LinkedList acts as the top of the stack.
How it works:
- Each node stores a value and a reference to the node below it
- Push creates a new node and makes it the new head
- Pop removes the head node and returns its value
- Peek returns the head node's value without removing it
Key Points about LinkedList Implementation:
- Truly dynamic — each node allocated only when needed
- No wasted memory from pre-allocation
- Slightly more memory per element (each node carries a pointer)
- Ideal for stacks where size is completely unknown
8. Java's Built-in Stack Class
Java provides a ready-made Stack class inside java.util. It extends Vector and is thread-safe by default.
Important Note: In modern Java development, it is often recommended to use Deque (specifically ArrayDeque) instead of Stack for better performance, since Stack is synchronized and carries the overhead of Vector.
9. Comparison of All Implementations
| Feature | Array | ArrayList | LinkedList | Java Stack | ArrayDeque |
| Size | Fixed | Dynamic | Dynamic | Dynamic | Dynamic |
| Stack Overflow Risk | Yes | No | No | No | No |
| Memory Usage | Pre-allocated | Auto-grows | Per-node overhead | Auto-grows | Auto-grows |
| Push Time | O(1) | O(1) amortized | O(1) | O(1) | O(1) |
| Pop Time | O(1) | O(1) | O(1) | O(1) | O(1) |
| Peek Time | O(1) | O(1) | O(1) | O(1) | O(1) |
| Thread Safe | No | No | No | Yes | No |
| Best For | Known size, max speed | General use | Unknown/huge size | Legacy code | Modern Java |
10. Advantages & Disadvantages
Advantages
| Advantage | Explanation |
| Simple to implement | Very few rules and operations to worry about |
| O(1) operations | Push, pop, and peek are all constant time |
| Memory efficient | No extra pointers needed (array-based) |
| Supports recursion | The call stack is itself a stack |
| Easy undo/redo | Natural fit for reversible action tracking |
| Backtracking | Perfectly suited for maze, puzzle, and game solving |
| Expression evaluation | Powers compilers and calculators |
Disadvantages
| Disadvantage | Explanation |
| Limited access | Cannot access elements in the middle directly |
| Fixed size (array) | Array-based stacks overflow if size is exceeded |
| No random access | You cannot do stack[2] — only top is accessible |
| Memory waste (array) | Pre-allocated array wastes space if underused |
| Not suitable for all problems | Many problems need queues, trees, or graphs instead |
| Stack overflow in recursion | Very deep recursion can overflow the JVM call stack |
11. Real-World Use Cases of Stack
Understanding when to use a stack is just as important as knowing how to implement one. Here is where stacks show up in real software:
Function Call Management (Call Stack) Every time your Java program calls a method, the JVM pushes that method's frame onto the call stack. When the method returns, the frame is popped. This is why you see "StackOverflowError" when you write infinite recursion.
Undo and Redo Operations Text editors, image editors (Photoshop), and IDEs use two stacks — one for undo history and one for redo history. Every action pushes onto the undo stack. Ctrl+Z pops from it and pushes to the redo stack.
Browser Navigation Your browser maintains a back-stack and a forward-stack. Visiting a new page pushes to the back-stack. Pressing Back pops from it and pushes to the forward-stack.
Expression Evaluation and Conversion Compilers use stacks to evaluate arithmetic expressions and convert between infix, prefix, and postfix notations. For example: 3 + 4 * 2 must be evaluated considering operator precedence — this is done with a stack.
Balanced Parentheses Checking Linters, compilers, and IDEs use stacks to check if brackets are balanced: {[()]} is valid, {[(])} is not.
Backtracking Algorithms Maze solving, N-Queens, Sudoku solvers, and depth-first search all use stacks (explicitly or via recursion) to backtrack to previous states when a path fails.
Syntax Parsing Compilers parse source code using stacks to match opening and closing constructs like if/else, try/catch, { and }.
12. Practice Problems with Full Solutions
Here is where things get really interesting. These problems will sharpen your stack intuition and prepare you for coding interviews.
Problem 1 — Reverse a String Using a Stack
Difficulty: Easy
Problem: Write a Java program to reverse a string using a Stack.
Approach: Push every character of the string onto a stack, then pop them all. Since LIFO reverses the order, the characters come out reversed.
Problem 2 — Check Balanced Parentheses
Difficulty: Easy–Medium
Problem: Given a string containing (, ), {, }, [, ], determine if the brackets are balanced.
Approach: Push every opening bracket onto the stack. When you see a closing bracket, check if it matches the top of the stack. If it does, pop. If it does not, the string is unbalanced.
Problem 3 — Reverse a Stack (Without Extra Data Structure)
Difficulty: Medium–Hard
Problem: Reverse all elements of a stack using only recursion — no array or extra stack allowed.
Approach: This is a classic recursion problem. You need two recursive functions:
insertAtBottom(stack, item)— inserts an element at the very bottom of the stackreverseStack(stack)— pops all elements, reverses, and uses insertAtBottom to rebuild
Problem 4 — Evaluate a Postfix Expression
Difficulty: Medium
Problem: Evaluate a postfix (Reverse Polish Notation) expression. Example: "2 3 4 * +" should return 14 because it is 2 + (3 * 4).
Approach: Scan left to right. If you see a number, push it. If you see an operator, pop two numbers, apply the operator, and push the result.
Problem 5 — Next Greater Element
Difficulty: Medium
Problem: For each element in an array, find the next greater element to its right. If none exists, output -1.
Example: Input: [4, 5, 2, 10, 8] → Output: [5, 10, 10, -1, -1]
Approach: Iterate right to left. Maintain a stack of candidates. For each element, pop all stack elements that are smaller than or equal to it — they can never be the answer for any element to the left. The top of the stack (if not empty) is the next greater element.
Problem 6 — Sort a Stack Using Recursion
Difficulty: Hard
Problem: Sort a stack in ascending order (smallest on top) using only recursion — no loops, no extra data structure.
13. Summary & Key Takeaways
A stack is a simple, elegant, and powerful data structure. Here is everything in one place:
What it is: A linear data structure that follows LIFO — Last In, First Out.
Core operations: push (add to top), pop (remove from top), peek (view top), isEmpty — all in O(1) time.
Three ways to implement it in Java:
- Array-based: fast, fixed size, risk of overflow
- ArrayList-based: dynamic, easy, slightly more overhead
- LinkedList-based: truly dynamic, memory-efficient per-element, best for unknown sizes
When to use it:
- Undo/redo systems
- Browser navigation
- Balancing brackets and parentheses
- Evaluating mathematical expressions
- Backtracking problems
- Managing recursive function calls
- Depth-first search
When NOT to use it:
- When you need random access to elements
- When insertion/deletion is needed from both ends (use Deque)
- When you need to search efficiently (use HashMap or BST)
Modern Java recommendation: Prefer ArrayDeque over the legacy Stack class for non-thread-safe scenarios. Use Stack only when you need synchronized access.
The stack is one of those data structures that once you truly understand, you start seeing it everywhere — in your browser, in your IDE, in recursive algorithms, and deep within the operating system itself.
This article covered everything from the fundamentals of the Stack data structure to multiple Java implementations, time complexity analysis, real-world applications, and six practice problems of increasing difficulty. Bookmark it as a reference and revisit the practice problems regularly — they are the real test of your understanding.
