Skip to main content

Stack & Queue

Operations complexity

Stack

OperationComplexity
push()O(1)
pop()O(1)
top()O(1)

Queue

OperationComplexity
enqueue()O(1)
dequeue()O(1)
peek()O(1)

Stack

The characteristic that makes something a "stack" is that we can only add and remove elements from the same end. It doesn't matter how we implement it, a "stack" is just an abstract interface.

For algorithm problems, a stack is a good option whenever we can recognize the LIFO pattern. Usually, there will be some component of the problem that involves elements in the input interacting with each other

Example problem:

class Solution {

public String removeDuplicates(String s) {
StringBuilder stack = new StringBuilder();
for (char c : s.toCharArray()) {
if (!stack.isEmpty() && stack.charAt(stack.length() - 1) == c)
stack.deleteCharAt(stack.length() - 1);
else
stack.append(c);
}

return stack.toString();
}
}

Queue

For algorithm problems, queues are less common than stacks, and the problems are general.

Example problem 933. Number of Recent Calls:

class RecentCounter {

Queue<Integer> queue;

public RecentCounter() {
queue = new LinkedList<>();
}

public int ping(int t) {
while (!queue.isEmpty() && queue.peek() < t - 3000) {
queue.poll();
}

queue.offer(t);
return queue.size();
}
}

Monotonic

Monotonic stacks and queues are useful in problems that, for each element, involves finding the " next" element based on some criteria, for example, the next greater element. They're also good when you have a dynamic window of elements and you want to maintain knowledge of the maximum or minimum element as the window changes. In more advanced problems, sometimes a monotonic stack or queue is only one part of the algorithm.

Pseudocode:

stack = []
for num in nums:
while stack.length > 0 AND stack.top >= num:
stack.pop()
// Some logic depending on the problem
stack.push(num)

Example problem 739. Daily Temperatures:

class Solution {

public int[] dailyTemperatures(int[] temperatures) {
Stack<Integer> stack = new Stack<>();
int[] answer = new int[temperatures.length];

for (int i = 0; i < temperatures.length; i++) {
while (!stack.empty() && temperatures[stack.peek()] < temperatures[i]) {
int j = stack.pop();
answer[j] = i - j;
}

stack.push(i);
}

return answer;
}
}

Fast and slow pointers

// head is the head node of a linked list
function fn(head):
slow = head
fast = head

while fast and fast.next:
Do something here
slow = slow.next
fast = fast.next.next