
Stack Data Structure Explained
A Stack is a linear data structure that follows the Last In, First Out principle, also known as LIFO. This means that the last element added to the stack is the first element removed from it.
Stacks are simple, but they are very important in computer science. They are used in function calls, undo systems, browser history, expression evaluation, syntax parsing, backtracking, and many real software systems.
Introduction
Data structures help developers organize and manage data efficiently. Arrays, linked lists, queues, stacks, trees, graphs, and hash tables all solve different types of problems.
A Stack is one of the most basic and useful data structures. It is easy to understand because it behaves like a real stack of plates. When you add a new plate, you place it on top. When you remove a plate, you remove the top plate first.
This behavior appears in many programming situations. Whenever the most recent item should be handled first, a Stack is often a clean and natural solution.
What Is a Stack?
A Stack is a collection of elements where insertion and removal happen from the same end. This end is usually called the top of the stack.
The Stack follows the LIFO rule:
Last In, First OutThis means the most recently added element is the first one to be removed.
The main idea is simple:
Add new elements to the top of the stack.
Remove elements from the top of the stack.
Look at the top element without removing it when needed.
Check whether the stack is empty before removing or reading the top element.
A Stack does not focus on random access. Its main purpose is to control access through the top.
Real-Life Analogy of a Stack
A common real-life example of a Stack is a pile of plates.
Imagine plates placed one above another:
Top → Plate C
Plate B
Plate APlate A was placed first. Plate B was placed after it. Plate C was placed last.
If we need to remove a plate, we remove Plate C first because it is on top. This matches the LIFO principle.
Last added: Plate C
First removed: Plate CThis simple idea is exactly how a Stack works in programming.
The LIFO Principle
LIFO stands for Last In, First Out. It means that the last item inserted into the stack is the first item removed from the stack.
For example, if we push values in this order:
10, 20, 30The stack will look like this:
Top → 30
20
10If we pop an element, the value 30 is removed first because it is at the top.
This behavior is different from a Queue, which follows FIFO, or First In, First Out.
Basic Stack Operations
A Stack usually supports a small set of important operations.
push: Adds an element to the top of the stack.
pop: Removes and returns the top element.
peek: Returns the top element without removing it.
isEmpty: Checks whether the stack has no elements.
size: Returns the number of elements in the stack.
These operations are enough to solve many problems where recent data must be processed first.
Push Operation
The push operation adds a new element to the top of the stack.
If the stack is empty and we push 10, the stack becomes:
Top → 10If we push 20, the stack becomes:
Top → 20
10If we push 30, the stack becomes:
Top → 30
20
10Every new element is placed above the previous top element.
Pop Operation
The pop operation removes the top element from the stack and returns it.
Consider this stack:
Top → 30
20
10If we call pop, the value 30 is removed.
Removed: 30The stack becomes:
Top → 20
10If we call pop again, the value 20 is removed.
Removed: 20The stack becomes:
Top → 10The pop operation must be used carefully when the stack is empty. Removing from an empty stack is usually called stack underflow.
Peek Operation
The peek operation returns the top element without removing it.
Consider this stack:
Top → 30
20
10If we call peek, the result is:
30But the stack remains unchanged:
Top → 30
20
10Peek is useful when we need to inspect the next element that would be removed, but we do not want to modify the stack.
isEmpty Operation
The isEmpty operation checks whether the stack contains any elements.
If the stack has no elements:
[]Then isEmpty returns true.
If the stack contains elements:
[10, 20, 30]Then isEmpty returns false.
This operation is important before calling pop or peek because those operations need a top element to exist.
Manual Run Example
Let us manually run a sequence of Stack operations.
push(10)
push(20)
push(30)
peek()
pop()
push(40)
pop()
pop()We start with an empty stack:
Stack: []Step 1: push(10)
We add 10 to the top of the stack.
Stack:
Top → 10Step 2: push(20)
We add 20 above 10.
Stack:
Top → 20
10Step 3: push(30)
We add 30 above 20.
Stack:
Top → 30
20
10Step 4: peek()
The top element is 30.
peek() returns 30The stack does not change:
Stack:
Top → 30
20
10Step 5: pop()
The top element 30 is removed.
pop() returns 30The stack becomes:
Stack:
Top → 20
10Step 6: push(40)
We add 40 to the top.
Stack:
Top → 40
20
10Step 7: pop()
The top element 40 is removed.
pop() returns 40The stack becomes:
Stack:
Top → 20
10Step 8: pop()
The top element 20 is removed.
pop() returns 20The stack becomes:
Stack:
Top → 10This manual run shows the LIFO behavior clearly. The most recently pushed value is always the first one removed.
Stack Implementation Using an Array
A Stack is commonly implemented using an array. In this implementation, the end of the array represents the top of the stack.
For example:
[10, 20, 30]The value 30 is the top element because it is at the end of the array.
Using the end of the array is efficient because many programming languages can add and remove elements from the end quickly.
Stack Pseudocode
class Stack:
items = empty array
push(value):
add value to end of items
pop():
if items is empty:
return error
remove and return last item
peek():
if items is empty:
return error
return last item
isEmpty():
return length of items == 0
size():
return length of itemsThis pseudocode shows the main behavior of a Stack without depending on a specific programming language.
Stack Example in PHP
Here is a clean PHP implementation of a Stack using an array:
<?php
class Stack
{
private array $items = [];
public function push(mixed $value): void
{
$this->items[] = $value;
}
public function pop(): mixed
{
if ($this->isEmpty()) {
throw new RuntimeException('Cannot pop from an empty stack.');
}
return array_pop($this->items);
}
public function peek(): mixed
{
if ($this->isEmpty()) {
throw new RuntimeException('Cannot peek an empty stack.');
}
return $this->items[count($this->items) - 1];
}
public function isEmpty(): bool
{
return count($this->items) === 0;
}
public function size(): int
{
return count($this->items);
}
public function toArray(): array
{
return $this->items;
}
}
$stack = new Stack();
$stack->push(10);
$stack->push(20);
$stack->push(30);
echo $stack->peek(); // 30
echo PHP_EOL;
echo $stack->pop(); // 30
echo PHP_EOL;
echo $stack->pop(); // 20This implementation protects the stack from invalid operations by throwing an exception when pop or peek is called on an empty stack.
Stack Example in JavaScript
Here is a Stack implementation in JavaScript:
class Stack {
constructor() {
this.items = [];
}
push(value) {
this.items.push(value);
}
pop() {
if (this.isEmpty()) {
throw new Error('Cannot pop from an empty stack.');
}
return this.items.pop();
}
peek() {
if (this.isEmpty()) {
throw new Error('Cannot peek an empty stack.');
}
return this.items[this.items.length - 1];
}
isEmpty() {
return this.items.length === 0;
}
size() {
return this.items.length;
}
toArray() {
return [...this.items];
}
}
const stack = new Stack();
stack.push(10);
stack.push(20);
stack.push(30);
console.log(stack.peek()); // 30
console.log(stack.pop()); // 30
console.log(stack.pop()); // 20The JavaScript implementation follows the same logic as the PHP version. The internal array stores the values, and the top of the stack is the end of the array.
Stack Implementation Using a Linked List
A Stack can also be implemented using a Linked List. In this version, the head of the linked list represents the top of the stack.
When we push a value, we insert a new node at the beginning. When we pop, we remove the first node.
Top → [30] → [20] → [10] → nullThis implementation is useful when we want dynamic node-based memory behavior.
Linked List Stack Example in JavaScript
class StackNode {
constructor(data) {
this.data = data;
this.next = null;
}
}
class LinkedListStack {
constructor() {
this.top = null;
this.length = 0;
}
push(value) {
const newNode = new StackNode(value);
newNode.next = this.top;
this.top = newNode;
this.length++;
}
pop() {
if (this.isEmpty()) {
throw new Error('Cannot pop from an empty stack.');
}
const removedValue = this.top.data;
this.top = this.top.next;
this.length--;
return removedValue;
}
peek() {
if (this.isEmpty()) {
throw new Error('Cannot peek an empty stack.');
}
return this.top.data;
}
isEmpty() {
return this.top === null;
}
size() {
return this.length;
}
}In this version, push and pop both work at the head of the linked list, so they are efficient.
Array Stack vs Linked List Stack
Both arrays and linked lists can be used to implement a Stack.
An array-based stack is usually simpler and works well in most high-level programming languages. A linked-list-based stack is useful when learning node-based structures or when dynamic memory behavior is important.
Main differences include:
Array stack is easier to implement.
Linked list stack uses nodes and references.
Array stack may occasionally resize internally.
Linked list stack allocates memory node by node.
Both can support push and pop in O(1) time.
For most practical JavaScript and PHP examples, an array-based stack is clear and enough.
Time Complexity of Stack Operations
Time complexity describes how the running time of an operation grows as the number of elements increases.
For a standard Stack, the main operations usually have these complexities:
push: O(1)
pop: O(1)
peek: O(1)
isEmpty: O(1)
size: O(1)
search: O(n)
Push, pop, and peek are constant-time operations because they work directly with the top of the stack.
Why Push Is O(1)
The push operation adds a new element to the top of the stack. It does not need to scan all elements.
For example:
Before:
Top → 20
10
push(30)
After:
Top → 30
20
10Only the top changes, so the operation takes constant time:
O(1)Why Pop Is O(1)
The pop operation removes the top element. It does not need to search for the element because the top is already known.
Before:
Top → 30
20
10
pop()
After:
Top → 20
10Only the top is removed or updated, so the operation takes constant time:
O(1)Why Search Is O(n)
Searching inside a Stack is not a standard Stack operation because Stacks are designed to access only the top element.
If we need to search for a value, we may need to inspect elements one by one.
Top → 30
20
10To search for 10, the algorithm may need to check 30, then 20, then 10. In the worst case, it checks all elements.
Therefore, searching is:
O(n)Space Complexity of a Stack
The space complexity of a Stack is O(n), where n is the number of elements stored in the stack.
Each pushed element requires memory. If the stack contains 100 values, it stores 100 values. If it contains 1,000 values, it stores 1,000 values.
Space complexity = O(n)The operations themselves usually use only a small amount of additional memory, but the data structure stores n elements.
Stack Overflow and Stack Underflow
Two important Stack-related errors are stack overflow and stack underflow.
Stack overflow happens when a stack tries to grow beyond its allowed memory limit. This can happen in low-level fixed-size stacks or with too many recursive function calls.
Stack underflow happens when a program tries to pop or peek from an empty stack.
Stack: []
pop() → underflow errorGood Stack implementations should handle underflow safely by returning null, returning an error value, or throwing an exception.
Stacks and Function Calls
One of the most important real uses of a Stack is managing function calls. Programming languages use a call stack to remember active function calls.
When a function is called, a new stack frame is pushed onto the call stack. When the function finishes, its stack frame is popped.
main()
calls login()
calls validateInput()The call stack may look like this:
Top → validateInput()
login()
main()When validateInput() finishes, it is removed first. Then login() continues. This follows LIFO.
Stacks and Recursion
Recursion depends heavily on the call stack. Every recursive call adds a new function frame to the stack.
For example, a recursive factorial function creates multiple calls:
factorial(4)
factorial(3)
factorial(2)
factorial(1)The last call must finish first, then control returns to the previous calls.
This is why recursion can cause stack overflow if the recursion depth becomes too large.
Stacks and Undo Systems
Stacks are commonly used to implement undo behavior.
Imagine a text editor where the user performs these actions:
Type A
Type B
Delete BEach action can be pushed onto an undo stack:
Top → Delete B
Type B
Type AWhen the user presses undo, the most recent action is reversed first. This is exactly LIFO behavior.
Stacks and Browser History
Browser history can also be understood using stacks.
When a user visits pages:
Home → About → Blog → ContactThe back button should return to the most recently visited previous page.
Top → Contact
Blog
About
HomeWhen the user goes back, Contact is removed first, then Blog becomes the current previous page.
Stacks and Expression Evaluation
Stacks are used in evaluating mathematical expressions and checking syntax.
For example, when checking balanced parentheses:
(a + b) * (c + d)The algorithm pushes opening brackets onto the stack and pops them when matching closing brackets appear.
If every opening bracket has a matching closing bracket in the correct order, the expression is balanced.
Balanced Parentheses Example
Let us check this expression:
{[()]}The process is:
Read { → push {
Read [ → push [
Read ( → push (
Read ) → pop (, match found
Read ] → pop [, match found
Read } → pop {, match foundAt the end, the stack is empty, so the brackets are balanced.
If the expression were:
{[(])}The closing bracket would not match the most recent opening bracket, so the expression would be invalid.
Balanced Parentheses in JavaScript
function isBalanced(expression) {
const stack = [];
const pairs = {
')': '(',
']': '[',
'}': '{'
};
for (const char of expression) {
if (char === '(' || char === '[' || char === '{') {
stack.push(char);
}
if (char === ')' || char === ']' || char === '}') {
if (stack.length === 0) {
return false;
}
const top = stack.pop();
if (top !== pairs[char]) {
return false;
}
}
}
return stack.length === 0;
}
console.log(isBalanced('{[()]}')); // true
console.log(isBalanced('{[(])}')); // falseThis is a classic Stack problem because the most recent opening bracket must be closed first.
Practical Example: Undo Stack in JavaScript
Here is a simple undo stack example:
class UndoManager {
constructor() {
this.undoStack = [];
}
addAction(action) {
this.undoStack.push(action);
}
undo() {
if (this.undoStack.length === 0) {
return null;
}
return this.undoStack.pop();
}
}
const manager = new UndoManager();
manager.addAction('Type A');
manager.addAction('Type B');
manager.addAction('Delete B');
console.log(manager.undo()); // Delete B
console.log(manager.undo()); // Type BThe most recent action is always undone first.
When Should Developers Use a Stack?
Developers should use a Stack when the most recent item should be processed first.
Stacks are useful when:
Undo behavior is needed.
Backtracking is needed.
Function calls or recursion are involved.
Syntax parsing is required.
Expression evaluation is needed.
Browser-like history behavior is needed.
Depth-first search needs to be implemented iteratively.
The main signal is LIFO behavior. If the newest item should be handled first, a Stack may be the right structure.
When Not to Use a Stack
A Stack is not the right structure when elements need to be processed in the same order they were added.
Developers should avoid a Stack when:
First-in, first-out behavior is required.
Fast random access by index is needed.
Elements need to be searched frequently.
Priority-based processing is required.
The oldest item should be processed first.
In those cases, a Queue, Array, Hash Table, or Priority Queue may be more suitable.
Stack vs Queue
Stacks and Queues are both linear data structures, but they follow different rules.
A Stack follows LIFO:
Last In, First OutA Queue follows FIFO:
First In, First OutFor example, a Stack is like a pile of plates. The last plate added is removed first.
A Queue is like a line of people waiting for service. The first person who enters the line is served first.
Main differences include:
Stack removes the newest item first.
Queue removes the oldest item first.
Stack uses push and pop.
Queue uses enqueue and dequeue.
Stack is useful for undo and backtracking.
Queue is useful for scheduling and waiting lines.
Common Mistakes When Learning Stacks
Beginners often understand the basic idea of a Stack, but they may still make implementation mistakes.
Common mistakes include:
Confusing Stack with Queue.
Removing from the wrong end of the array.
Forgetting to handle empty stack cases.
Using Stack when random access is needed.
Assuming search is O(1).
Ignoring stack overflow in recursive solutions.
Changing the stack when only peek is needed.
The safest rule is simple: only the top element should be directly accessed.
Practical Checklist for Understanding Stacks
Before moving to Queues, Trees, Graphs, or advanced algorithms, developers should be able to answer these questions:
What does LIFO mean?
What is the top of a Stack?
What does push do?
What does pop do?
What does peek do?
Why are push and pop O(1)?
What is stack underflow?
How are stacks used in function calls?
Why are stacks useful for undo systems?
What is the difference between a Stack and a Queue?
If these questions are clear, the developer understands the real logic behind Stacks.
Advantages of Stacks
Stacks have several advantages.
They are simple to understand.
They are easy to implement.
Push and pop are O(1).
They naturally support LIFO behavior.
They are useful for undo, recursion, parsing, and backtracking.
They can be implemented using arrays or linked lists.
These advantages make Stacks one of the most important data structures for beginners and professionals.
Disadvantages of Stacks
Stacks also have limitations.
They do not provide fast random access.
They are not suitable for FIFO behavior.
Searching inside a Stack is O(n).
They can cause overflow if memory limits are reached.
They are restrictive because only the top element is directly accessible.
These limitations are not problems when a Stack is used for the right purpose. They simply define when another data structure may be better.
Stacks in Real Software Projects
Stacks appear in real software projects in many forms, even when developers do not create a Stack class manually.
Common real-world uses include:
Call stack management in programming languages.
Undo and redo features in editors.
Browser back navigation.
Syntax parsing in compilers.
Expression evaluation in calculators.
Backtracking algorithms.
Depth-first search in graphs.
Routing history in applications.
Understanding Stacks helps developers understand how these systems work internally.
Conclusion
A Stack is a linear data structure that follows the Last In, First Out principle. The last element added to the stack is the first element removed.
The main Stack operations are push, pop, peek, isEmpty, and size. Push adds an element to the top, pop removes the top element, and peek reads the top element without removing it.
Stacks usually provide O(1) time complexity for push, pop, peek, isEmpty, and size operations. Searching inside a Stack takes O(n) because the structure is not designed for random access.
Stacks can be implemented using arrays or linked lists. They are used in function calls, recursion, undo systems, browser history, expression evaluation, syntax checking, and backtracking.
For developers learning Data Structures and Algorithms, Stacks are essential. They teach controlled access, LIFO behavior, memory thinking, and the foundation for many algorithmic techniques used in real software systems.

