title: 132 Pattern
slug: 132-pattern
difficulty: medium
leetcode_id: 456
leetcode_url: https://leetcode.com/problems/132-pattern/
categories:
  - arrays
  - stack
patterns:
  - monotonic-stack

function_signature: "def find132pattern(nums: list[int]) -> bool:"

test_cases:
  visible:
    - input: { nums: [1, 2, 3, 4] }
      expected: false
    - input: { nums: [3, 1, 4, 2] }
      expected: true
    - input: { nums: [-1, 3, 2, 0] }
      expected: true
  hidden:
    - input: { nums: [1, 2] }
      expected: false
    - input: { nums: [3, 5, 0, 3, 4] }
      expected: true
    - input: { nums: [1, 0, 1, -4, -3] }
      expected: false
    - input: { nums: [1, 2, 2, 2] }
      expected: false
    - input: { nums: [-2, 1, 2, -2, 1, 2] }
      expected: true
    - input: { nums: [1, 3, 2, 4, 5, 6, 7, 8, 9, 10] }
      expected: true

description: |
  Given an array of `n` integers `nums`, a **132 pattern** is a subsequence of three integers `nums[i]`, `nums[j]` and `nums[k]` such that `i < j < k` and `nums[i] < nums[k] < nums[j]`.

  Return `true` *if there is a **132 pattern** in* `nums`, *otherwise, return* `false`.

  The "132" name comes from the value ordering: the 1<sup>st</sup> element is smallest, the 3<sup>rd</sup> element (middle index) is largest, and the 2<sup>nd</sup> element (last index) is in between.

constraints: |
  - `n == nums.length`
  - `1 <= n <= 2 * 10^5`
  - `-10^9 <= nums[i] <= 10^9`

examples:
  - input: "nums = [1,2,3,4]"
    output: "false"
    explanation: "There is no 132 pattern in the sequence. The array is strictly increasing, so we can never find nums[k] < nums[j] where k > j."
  - input: "nums = [3,1,4,2]"
    output: "true"
    explanation: "There is a 132 pattern in the sequence: [1, 4, 2]. Here i=1, j=2, k=3, and nums[1]=1 < nums[3]=2 < nums[2]=4."
  - input: "nums = [-1,3,2,0]"
    output: "true"
    explanation: "There are three 132 patterns in the sequence: [-1, 3, 2], [-1, 3, 0] and [-1, 2, 0]."

explanation:
  intuition: |
    Imagine you're looking for a specific "shape" in a sequence of numbers: a valley followed by a peak, with something in between on the right side.

    The pattern we seek is **"small, big, medium"** in terms of values, appearing at indices **i < j < k**. Think of it like finding a mountain range where:
    - Position `i` is in a valley (smallest value)
    - Position `j` is at the peak (largest value)
    - Position `k` is partway up the mountain (between the two)

    The key insight is that if we scan from **right to left**, we can maintain candidates for the "2" (middle value, `nums[k]`) using a **monotonic stack**. As we pop elements from the stack, we're finding elements that could serve as valid `nums[k]` values — elements that are smaller than some `nums[j]` to their left.

    By tracking the largest popped value (our best candidate for `nums[k]`), we just need to find any `nums[i]` to the left that's smaller than this candidate. If we find one, we've completed the 132 pattern!

  approach: |
    We solve this using a **Monotonic Stack (Right to Left)** approach:

    **Step 1: Initialise tracking variables**

    - `stack`: A monotonic decreasing stack to track potential `nums[j]` candidates
    - `third`: The best candidate for `nums[k]` (the "2" in 132), initialised to negative infinity

    &nbsp;

    **Step 2: Iterate from right to left**

    - For each element `num` at position `i`, we check if it could be `nums[i]` (the "1" in 132)
    - If `num < third`, we found a valid pattern! The current `num` is smaller than our `third` candidate, and `third` came from being popped by some larger element (which serves as `nums[j]`)
    - Otherwise, maintain the monotonic stack:
      - While `num` is greater than the stack top, pop elements
      - Each popped element becomes a candidate for `third` (keep the maximum)
      - Push `num` onto the stack

    &nbsp;

    **Step 3: Return the result**

    - If we found `num < third` at any point, return `true`
    - If we finish the loop without finding a pattern, return `false`

    &nbsp;

    Why does this work? When we pop an element from the stack, it means we found a larger element to its left (the current `num`). This popped element becomes a valid `third` because there exists some `nums[j] > nums[k]` with `j < k`. We then just need `nums[i] < nums[k]` with `i < j`.

  common_pitfalls:
    - title: The Brute Force Trap
      description: |
        A natural first approach is three nested loops checking all combinations of `i < j < k`:

        ```python
        for i in range(n):
            for j in range(i+1, n):
                for k in range(j+1, n):
                    if nums[i] < nums[k] < nums[j]:
                        return True
        ```

        This is **O(n³)** and with `n <= 2 * 10^5`, that's up to 8 × 10^15 operations — guaranteed **Time Limit Exceeded**.
      wrong_approach: "Triple nested loops checking all triplets"
      correct_approach: "Monotonic stack achieving O(n) time"

    - title: Confusing the Index Order vs Value Order
      description: |
        The "132" refers to the **value ordering**, not the index ordering:
        - Index order: `i < j < k` (always left to right)
        - Value order: `nums[i] < nums[k] < nums[j]` (small, medium, big → positions 1, 3, 2)

        A common mistake is looking for values in ascending order `nums[i] < nums[j] < nums[k]`, which would be a "123" pattern instead.
      wrong_approach: "Looking for strictly increasing subsequence"
      correct_approach: "Looking for small-big-medium pattern at indices i < j < k"

    - title: Forgetting to Track the Maximum Popped Element
      description: |
        When using the monotonic stack approach, some implementations only check the most recently popped element. However, we need to track the **maximum** of all popped elements.

        Consider `[1, 0, 1, -4, -3]`: we need to ensure our `third` candidate is the best possible one to maximise our chances of finding a valid `nums[i]`.
      wrong_approach: "Only using the last popped element"
      correct_approach: "Tracking the maximum of all popped elements as third"

    - title: Handling Duplicate Values
      description: |
        The pattern requires **strict inequalities**: `nums[i] < nums[k] < nums[j]`. Equal values don't count.

        For `[1, 2, 2, 2]`, there's no valid 132 pattern because we can't have `nums[k] < nums[j]` when they're equal.
      wrong_approach: "Using <= comparisons"
      correct_approach: "Using strict < comparisons throughout"

  key_takeaways:
    - "**Monotonic stack mastery**: This problem demonstrates how a monotonic stack can track relationships between elements in O(n) time"
    - "**Right-to-left traversal**: Sometimes scanning backwards simplifies the logic — here it lets us build up `third` candidates before we need them"
    - "**The popped element insight**: Elements popped from a monotonic stack have a guaranteed relationship with the element that caused the pop"
    - "**Pattern recognition**: The 132 pattern appears in many variations (stock prices, sequences) — recognising this structure is valuable"

  time_complexity: "O(n). Each element is pushed and popped from the stack at most once, giving us linear time."
  space_complexity: "O(n). In the worst case (strictly decreasing array), the stack holds all n elements."

solutions:
  - approach_name: Monotonic Stack (Right to Left)
    is_optimal: true
    code: |
      def find132pattern(nums: list[int]) -> bool:
          n = len(nums)
          if n < 3:
              return False

          stack = []  # Monotonic decreasing stack for nums[j] candidates
          third = float('-inf')  # Best candidate for nums[k] (the "2" in 132)

          # Scan from right to left
          for i in range(n - 1, -1, -1):
              # If current num is less than third, we found the pattern!
              # Current num is nums[i], third is nums[k], and nums[j] exists
              # (it's the element that popped third from the stack)
              if nums[i] < third:
                  return True

              # Maintain monotonic decreasing stack
              # Pop smaller elements — they become candidates for third
              while stack and nums[i] > stack[-1]:
                  third = stack.pop()  # Update third to the largest popped value

              # Push current element as potential nums[j]
              stack.append(nums[i])

          return False
    explanation: |
      **Time Complexity:** O(n) — Each element is pushed and popped at most once.

      **Space Complexity:** O(n) — Stack can hold up to n elements.

      The key insight is that when we pop elements from the stack, those elements are smaller than the current element (potential `nums[j]`), making them valid candidates for `nums[k]`. We track the largest such candidate in `third`. Then any element smaller than `third` that we encounter later completes the pattern.

  - approach_name: Prefix Minimum with Stack
    is_optimal: true
    code: |
      def find132pattern(nums: list[int]) -> bool:
          n = len(nums)
          if n < 3:
              return False

          # Precompute minimum from the left for each position
          min_left = [0] * n
          min_left[0] = nums[0]
          for i in range(1, n):
              min_left[i] = min(min_left[i - 1], nums[i])

          # Use stack to find valid j, k pairs where min_left[j] < nums[k] < nums[j]
          stack = []  # Stack of indices for potential nums[k]

          # Scan from right to left
          for j in range(n - 1, -1, -1):
              # We need nums[i] < nums[k] < nums[j]
              # min_left[j] gives us the minimum nums[i] to the left of j
              if nums[j] > min_left[j]:
                  # Pop elements that are too small to be nums[k]
                  while stack and nums[stack[-1]] <= min_left[j]:
                      stack.pop()

                  # Check if top of stack is a valid nums[k]
                  if stack and nums[stack[-1]] < nums[j]:
                      return True

                  stack.append(j)

          return False
    explanation: |
      **Time Complexity:** O(n) — Two passes: one for prefix minimum, one with stack.

      **Space Complexity:** O(n) — For the prefix minimum array and stack.

      This approach explicitly tracks the minimum to the left of each position. For each potential `nums[j]`, we know exactly what `nums[i]` would need to beat (`min_left[j]`). The stack maintains candidates for `nums[k]` that satisfy the constraints.

  - approach_name: Brute Force
    is_optimal: false
    code: |
      def find132pattern(nums: list[int]) -> bool:
          n = len(nums)

          for j in range(1, n - 1):
              # Find minimum to the left of j
              min_i = min(nums[:j])

              # Check if any element to the right satisfies the pattern
              for k in range(j + 1, n):
                  if min_i < nums[k] < nums[j]:
                      return True

          return False
    explanation: |
      **Time Complexity:** O(n²) — For each j, we scan left for min and right for valid k.

      **Space Complexity:** O(1) — No additional data structures.

      This optimised brute force fixes `j` as the middle element, finds the minimum to its left, then searches for a valid `nums[k]` to its right. While better than O(n³), it's still too slow for large inputs and will TLE on LeetCode. Included to show the progression toward the optimal solution.
