Day 4: Stack and Queue Problems

Here are the detailed notes for The focus today is understanding how to implement stacks and queues, common operations, and solving key problems like using one to implement the other (stack using queues and vice versa), as well as solving problems like valid parentheses.

1. Stacks Overview

  • Definition: A stack is a linear data structure that follows the Last In First Out (LIFO) principle. The last element added is the first one to be removed.

  • Common Operations:

    • Push(x): Add an element x to the top of the stack.

    • Pop(): Remove and return the element from the top of the stack.

    • Peek()/Top(): View the element at the top of the stack without removing it.

    • isEmpty(): Check if the stack is empty.

  • Applications:

    • Function calls: The call stack in programming languages is an example of stack usage.

    • Undo operations: Stacks are often used to store states for undo functionality.

    • Expression evaluation: Parsing expressions in postfix, infix, or prefix notation.


2. Queues Overview

  • Definition: A queue is a linear data structure that follows the First In First Out (FIFO) principle. The first element added is the first one to be removed.

  • Common Operations:

    • Enqueue(x): Add an element x to the end of the queue.

    • Dequeue(): Remove and return the element from the front of the queue.

    • Peek()/Front(): View the element at the front of the queue without removing it.

    • isEmpty(): Check if the queue is empty.

  • Applications:

    • Task scheduling: Queues are used to manage tasks in systems like CPU scheduling.

    • Breadth-First Search (BFS) in graphs and trees.


3. Key Problems and Solutions

1. Implement a Stack using Queues

  • Problem Statement: Implement a stack using two queues.

  • Approach:

    • Use two queues (q1 and q2), where:

      • Push the element into the non-empty queue (q1 or q2).

      • For popping the top element, transfer all elements except the last one to the other queue.

  • Time Complexity:

    • Push operation: O(1).

    • Pop operation: O(n), as all elements (except the last one) are transferred between the two queues.

2. Implement a Queue using Stacks

  • Problem Statement: Implement a queue using two stacks.

  • Approach:

    • Use two stacks (stack_in and stack_out), where:

      • Enqueue: Push the element onto stack_in.

      • Dequeue: If stack_out is empty, transfer all elements from stack_in to stack_out, then pop from stack_out.

  • Time Complexity:

    • Enqueue operation: O(1).

    • Dequeue operation: Amortized O(1), because elements are only moved between stacks once.


4. Common Stack and Queue Problems

1. Valid Parentheses

  • Problem Statement: Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid.

  • Approach:

    • Use a stack to store the opening brackets.

    • For every closing bracket, check if it matches the top of the stack.

    • If the stack is empty at the end, the parentheses are balanced.

  • Time Complexity: O(n), where n is the length of the string.

  • Space Complexity: O(n), in the worst case where all elements are stored in the stack.

2. Min Stack

  • Problem Statement: Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.

  • Approach:

    • Use two stacks: one for the stack itself and one to track the minimum elements.

  • Time Complexity: O(1) for all operations.

  • Space Complexity: O(n), where n is the number of elements pushed.

3. Evaluate Reverse Polish Notation (Postfix Expression)

  • Problem Statement: Evaluate the value of an arithmetic expression in Reverse Polish Notation (RPN). Valid operators are +, -, *, and /. Each operand may be an integer or another expression.

  • Approach:

    • Use a stack to evaluate the expression. Push numbers onto the stack, and when encountering an operator, pop the top two numbers, apply the operation, and push the result back.

  • Time Complexity: O(n), where n is the length of the tokens.

  • Space Complexity: O(n), to store intermediate results.

4. Next Greater Element

  • Problem Statement: Given an array, find the next greater element for every element. The next greater element of an element x is the first greater element to the right of x in the array.

  • Approach:

    • Use a stack to keep track of elements for which we haven’t yet found a greater element.

    • Traverse the array from right to left, maintaining the current "next greater" element on the stack.

  • Time Complexity: O(n), where n is the length of the array.

  • Space Complexity: O(n), to store the stack.


5. Key Techniques for Stacks and Queues

  1. Using Two Data Structures:

    • Stack with a Supporting Stack: For problems like the min stack, use a secondary stack to track the minimum values.

    • Queue with Two Stacks: By reversing the order of elements with two stacks, you can implement a queue using stacks.

  2. Backtracking with Stacks:

    • Stacks are frequently used for backtracking problems (e.g., valid parentheses, depth-first search, etc.).

  3. Sliding Windows with Deques:

    • Deques (double-ended queues) are used for optimizing sliding window problems where you need quick access to both ends of the queue.


  1. LeetCode:

    • Valid Parentheses

    • Min Stack

    • Implement Queue using Stacks

    • Next Greater Element I

  2. HackerRank:

    • Balanced Brackets

    • Queue using Two Stacks

    • Maximum Element

By mastering stack and queue problems, and learning to implement one using the other, you will gain key insights into how data structures can be adapted to fit specific problem constraints.

Last updated