Stacks and Queues are behavior-based data structures.
They are not about how data is stored, but how data is accessed.
Many complex problems become easy once you identify whether they follow:
This module builds that instinct.
A stack follows Last In, First Out (LIFO) order.
Think of a stack of plates:
All operations run in O(1) time
class StackArray {
int[] stack;
int top;
StackArray(int size){
stack = new int[size];
top = -1;
}
void push(int x){
stack[++top] = x;
}
int pop(){
return stack[top--];
}
}
Limitations:
class StackLL {
Node top;
void push(int x){
Node node = new Node(x);
node.next = top;
top = node;
}
int pop(){
int val = top.data;
top = top.next;
return val;
}
}
Advantages:
Stacks are used to evaluate expressions because:
Expression: 23*54*+9-
Steps:
Stack<Integer> st = new Stack<>();
for(char c : exp.toCharArray()){
if(Character.isDigit(c))
st.push(c - '0');
else{
int b = st.pop();
int a = st.pop();
st.push(apply(a, b, c));
}
}
A + BAB++ABComputers evaluate postfix/prefix easily without parentheses.
while(!stack.isEmpty() && precedence(stack.peek()) >= precedence(curr)){
result += stack.pop();
}
Used heavily in:
Check if brackets are balanced.
Example:
{[()]} → Valid{[(])} → InvalidStack<Character> st = new Stack<>();
for(char c : s.toCharArray()){
if(c=='('||c=='{'||c=='[')
st.push(c);
else{
if(st.isEmpty()) return false;
char top = st.pop();
if(!match(top, c)) return false;
}
}
return st.isEmpty();
Time: O(n)
For each element, find the next element greater than it on the right.
Example:[4, 5, 2, 25] → [5, 25, 25, -1]
Stack<Integer> st = new Stack<>();
for(int i = n - 1; i >= 0; i--){
while(!st.isEmpty() && st.peek() <= arr[i])
st.pop();
res[i] = st.isEmpty() ? -1 : st.peek();
st.push(arr[i]);
}
Find number of consecutive days before today with price ≤ today.
for(int i = 0; i < n; i++){
while(!st.isEmpty() && price[st.peek()] <= price[i])
st.pop();
span[i] = st.isEmpty() ? i + 1 : i - st.peek();
st.push(i);
}
Classic interview problem.
A stack that maintains:
Used in:
Key insight:
Stack stores useful candidates only
Queue follows First In, First Out (FIFO).
Example:
Basic operations:
int[] q = new int[n];
int front = 0, rear = -1;
void enqueue(int x){
q[++rear] = x;
}
Problem:
Reuses freed space by wrapping around.
Concept:
(rear + 1) % sizeUsed in:
Insert and delete from both ends.
Operations:
Used in:
Elements are removed based on priority, not order.
Internally implemented using heap.
Example:
PriorityQueue<Integer> pq = new PriorityQueue<>();
pq.add(10);
pq.add(5);
pq.poll();
Used in:
Use two stacks:
void enqueue(int x){
in.push(x);
}
int dequeue(){
if(out.isEmpty()){
while(!in.isEmpty())
out.push(in.pop());
}
return out.pop();
}
Amortized O(1)
Find maximum in each window of size k.
Deque<Integer> dq = new ArrayDeque<>();
for(int i = 0; i < n; i++){
while(!dq.isEmpty() && dq.peekFirst() <= i - k)
dq.pollFirst();
while(!dq.isEmpty() && arr[dq.peekLast()] <= arr[i])
dq.pollLast();
dq.addLast(i);
if(i >= k - 1)
result[i - k + 1] = arr[dq.peekFirst()];
}
Time: O(n)
Breadth First Search explores nodes level by level.
Queue maintains order of exploration.
Queue<Integer> q = new LinkedList<>();
q.add(start);
while(!q.isEmpty()){
int node = q.poll();
for(int neigh : graph[node]){
q.add(neigh);
}
}
Used in: