456. 132 Pattern
Problem Description
You are given an array of n
integers called nums
. Your task is to determine if there exists a 132 pattern in this array.
A 132 pattern is defined as a subsequence of three integers nums[i]
, nums[j]
, and nums[k]
that satisfy the following conditions:
- The indices must follow the order:
i < j < k
- The values must follow the pattern:
nums[i] < nums[k] < nums[j]
In other words, you need to find three numbers where:
- The first number (at position
i
) is the smallest - The second number (at position
j
) is the largest - The third number (at position
k
) is in the middle value-wise - These three numbers appear in this specific order in the array (though not necessarily consecutively)
The name "132 pattern" comes from the relative ordering of the values: if we label them as 1 (smallest), 3 (largest), and 2 (middle), they appear in the sequence as 1-3-2.
Return true
if such a pattern exists in the array, otherwise return false
.
For example:
- In the array
[1, 2, 3, 4]
, there is no 132 pattern - In the array
[3, 1, 4, 2]
, there is a 132 pattern:nums[1]=1
,nums[2]=4
,nums[3]=2
where1 < 2 < 4
Intuition
The key insight is to traverse the array from right to left while keeping track of potential candidates for the middle value (the "2" in the 132 pattern).
Think about it this way: if we're looking for a pattern where nums[i] < nums[k] < nums[j]
with i < j < k
, we can fix our position and look for what comes before. When we traverse from right to left, we're essentially looking at potential nums[j]
values and trying to find if there's a valid nums[i]
that would complete the pattern.
The brilliant idea here is to maintain a variable vk
that represents the maximum possible value for nums[k]
(the middle value in our 132 pattern). As we traverse backwards:
-
If we encounter a value smaller than
vk
, we've found ournums[i]
! Why? Becausevk
already represents a validnums[k]
that came after somenums[j]
that's larger than it. So we havenums[i] < vk (nums[k]) < some previous nums[j]
. -
We use a stack to keep track of potential
nums[k]
values. When we see a new elementx
:- All stack elements smaller than
x
can potentially benums[k]
values (withx
being their correspondingnums[j]
) - We pop these smaller elements and update
vk
to the maximum among them - This ensures
vk
always holds the best (largest possible) middle value for any 132 pattern we've seen so far
- All stack elements smaller than
-
The stack maintains elements in decreasing order from bottom to top, which helps us efficiently find and update potential
nums[k]
values.
By processing from right to left, we ensure that when we find a valid nums[i]
(a value less than vk
), we already know that there exists a valid nums[j]
and nums[k]
to its right that form the 132 pattern.
Solution Approach
The implementation uses a monotonic stack approach with a clever backwards traversal:
-
Initialize variables:
vk = -inf
: Represents the maximum validnums[k]
(middle value) we've found so farstk = []
: A stack to maintain potentialnums[j]
values (the peak values)
-
Traverse the array backwards using
nums[::-1]
:- We process each element
x
from right to left
- We process each element
-
For each element
x
, check if we've found a 132 pattern:- If
x < vk
, we returnTrue
immediately - Why? Because
x
is ournums[i]
, and we already knowvk
is a validnums[k]
that has a correspondingnums[j] > nums[k]
to its right
- If
-
Update potential
nums[k]
values:- While the stack is not empty and
stk[-1] < x
:- Pop elements from the stack
- Update
vk
to the popped value
- This step identifies that
x
can be anums[j]
(peak), and all popped elements can be potentialnums[k]
values for this peak - We keep the maximum such value in
vk
- While the stack is not empty and
-
Maintain the stack:
- Append
x
to the stack - The stack maintains elements in non-increasing order (larger or equal elements on top)
- Append
-
Return result:
- If we complete the traversal without finding a pattern, return
False
- If we complete the traversal without finding a pattern, return
Example walkthrough with [3, 1, 4, 2]
:
- Start:
vk = -inf
,stk = []
- Process
2
:2 > -inf
, no elements to pop,stk = [2]
- Process
4
:4 > -inf
, pop2
(since2 < 4
),vk = 2
,stk = [4]
- Process
1
:1 < 2
(ourvk
), returnTrue
!
The algorithm efficiently finds the 132 pattern in O(n)
time and O(n)
space complexity.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's trace through the algorithm with the array [6, 12, 3, 4, 6, 11, 20]
.
Initial Setup:
vk = -inf
(tracks the maximum valid middle value "2" in 132 pattern)stk = []
(stack for potential peak values "3")- We'll traverse backwards:
[20, 11, 6, 4, 3, 12, 6]
Step-by-step traversal:
Process 20 (index 6):
- Check: Is
20 < -inf
? No - Stack is empty, nothing to pop
- Push 20 onto stack:
stk = [20]
vk = -inf
Process 11 (index 5):
- Check: Is
11 < -inf
? No - Is
stk[-1]=20 < 11
? No, don't pop - Push 11 onto stack:
stk = [20, 11]
vk = -inf
Process 6 (index 4):
- Check: Is
6 < -inf
? No - Is
stk[-1]=11 < 6
? No, don't pop - Push 6 onto stack:
stk = [20, 11, 6]
vk = -inf
Process 4 (index 3):
- Check: Is
4 < -inf
? No - Is
stk[-1]=6 < 4
? No, don't pop - Push 4 onto stack:
stk = [20, 11, 6, 4]
vk = -inf
Process 3 (index 2):
- Check: Is
3 < -inf
? No - Is
stk[-1]=4 < 3
? No, don't pop - Push 3 onto stack:
stk = [20, 11, 6, 4, 3]
vk = -inf
Process 12 (index 1):
- Check: Is
12 < -inf
? No - Is
stk[-1]=3 < 12
? Yes! Pop 3, setvk = 3
- Is
stk[-1]=4 < 12
? Yes! Pop 4, setvk = 4
- Is
stk[-1]=6 < 12
? Yes! Pop 6, setvk = 6
- Is
stk[-1]=11 < 12
? Yes! Pop 11, setvk = 11
- Is
stk[-1]=20 < 12
? No, stop popping - Push 12 onto stack:
stk = [20, 12]
- Now
vk = 11
(this represents a valid middle value with 12 as its peak)
Process 6 (index 0):
- Check: Is
6 < 11
? YES! - Return
True
Pattern found:
nums[0] = 6
(the "1" - smallest)nums[1] = 12
(the "3" - largest/peak)nums[5] = 11
(the "2" - middle)- The pattern is:
6 < 11 < 12
with indices0 < 1 < 5
✓
The algorithm correctly identifies that when we process element 6, it's smaller than our tracked middle value (vk = 11
). Since vk
was set when we processed 12 as a peak, we know there exists a valid 132 pattern.
Solution Implementation
1class Solution:
2 def find132pattern(self, nums: List[int]) -> bool:
3 """
4 Find if there exists a 132 pattern in the array.
5 A 132 pattern is a subsequence of three integers nums[i], nums[j], nums[k]
6 such that i < j < k and nums[i] < nums[k] < nums[j].
7
8 Algorithm: Traverse the array from right to left, maintaining:
9 - A stack to track potential nums[j] values (the peak of the pattern)
10 - A variable to track the maximum nums[k] value (the middle value)
11 """
12 # Initialize the maximum value for nums[k] (the middle element of 132 pattern)
13 max_k_value = float('-inf')
14
15 # Stack to maintain potential nums[j] values (peaks)
16 stack = []
17
18 # Traverse the array from right to left
19 for current_num in reversed(nums):
20 # If current number is less than max_k_value, we found a 132 pattern
21 # current_num acts as nums[i], max_k_value as nums[k],
22 # and the popped element that created max_k_value as nums[j]
23 if current_num < max_k_value:
24 return True
25
26 # Pop all elements smaller than current number from stack
27 # These become potential nums[k] values
28 while stack and stack[-1] < current_num:
29 max_k_value = stack.pop()
30
31 # Push current number to stack as a potential nums[j] (peak)
32 stack.append(current_num)
33
34 # No 132 pattern found
35 return False
36
1class Solution {
2 public boolean find132pattern(int[] nums) {
3 // Initialize the potential "middle value" (nums[k] in the 132 pattern)
4 // Using Integer.MIN_VALUE as initial value (more readable than bit shifting)
5 int middleValue = Integer.MIN_VALUE;
6
7 // Stack to maintain potential "maximum values" (nums[j] in the 132 pattern)
8 // Using monotonic decreasing stack approach
9 Deque<Integer> stack = new ArrayDeque<>();
10
11 // Traverse the array from right to left
12 // This allows us to maintain nums[j] and nums[k] while looking for nums[i]
13 for (int i = nums.length - 1; i >= 0; i--) {
14 // Check if current element can be nums[i] (smallest in 132 pattern)
15 // If nums[i] < middleValue, we found a valid 132 pattern
16 if (nums[i] < middleValue) {
17 return true;
18 }
19
20 // Update middleValue by popping smaller elements from stack
21 // These popped elements become candidates for nums[k] (middle value)
22 // Current nums[i] will act as nums[j] (maximum value)
23 while (!stack.isEmpty() && stack.peek() < nums[i]) {
24 middleValue = stack.pop();
25 }
26
27 // Push current element to stack as a potential nums[j] (maximum value)
28 stack.push(nums[i]);
29 }
30
31 // No 132 pattern found in the array
32 return false;
33 }
34}
35
1class Solution {
2public:
3 bool find132pattern(vector<int>& nums) {
4 // Variable to track the maximum value that can serve as 'nums[k]' in the pattern
5 // where nums[i] < nums[k] < nums[j] and i < j < k
6 int maxK = INT_MIN;
7
8 // Stack to maintain potential 'nums[j]' values in decreasing order
9 stack<int> candidateStack;
10
11 // Traverse the array from right to left
12 for (int i = nums.size() - 1; i >= 0; --i) {
13 // Check if current element can be 'nums[i]' in the 132 pattern
14 // If nums[i] < maxK, we found a valid pattern
15 if (nums[i] < maxK) {
16 return true;
17 }
18
19 // Update maxK by popping elements smaller than current nums[i]
20 // These popped elements become potential 'nums[k]' values
21 while (!candidateStack.empty() && candidateStack.top() < nums[i]) {
22 maxK = candidateStack.top();
23 candidateStack.pop();
24 }
25
26 // Push current element as a potential 'nums[j]' for future iterations
27 candidateStack.push(nums[i]);
28 }
29
30 // No 132 pattern found in the array
31 return false;
32 }
33};
34
1/**
2 * Finds if there exists a 132 pattern in the array.
3 * A 132 pattern is a subsequence of three integers nums[i], nums[j], nums[k]
4 * such that i < j < k and nums[i] < nums[k] < nums[j].
5 *
6 * @param nums - The input array of numbers
7 * @returns true if a 132 pattern exists, false otherwise
8 */
9function find132pattern(nums: number[]): boolean {
10 // Variable to track the maximum value of nums[k] in the 132 pattern
11 // where nums[k] is the middle value (less than nums[j] but greater than nums[i])
12 let maxMiddleValue: number = -Infinity;
13
14 // Stack to maintain potential nums[j] values (the peak of 132 pattern)
15 // in decreasing order from bottom to top
16 const stack: number[] = [];
17
18 // Traverse the array from right to left
19 for (let i: number = nums.length - 1; i >= 0; i--) {
20 // If current element is less than maxMiddleValue,
21 // we found a valid 132 pattern (current element is nums[i])
22 if (nums[i] < maxMiddleValue) {
23 return true;
24 }
25
26 // Pop elements from stack that are smaller than current element
27 // These popped elements become potential nums[k] values
28 while (stack.length > 0 && stack[stack.length - 1] < nums[i]) {
29 // Update maxMiddleValue with the popped value
30 // This ensures we track the largest possible nums[k]
31 maxMiddleValue = stack.pop()!;
32 }
33
34 // Push current element to stack as a potential nums[j] value
35 stack.push(nums[i]);
36 }
37
38 // No 132 pattern found in the array
39 return false;
40}
41
Time and Space Complexity
Time Complexity: O(n)
where n
is the length of the input array nums
.
The algorithm iterates through the array once in reverse order using nums[::-1]
, which takes O(n)
time to create the reversed view. Then, for each element in the reversed array:
- The comparison
x < vk
takesO(1)
time - The while loop might pop elements from the stack, but each element can be pushed and popped at most once throughout the entire execution
- The append operation takes
O(1)
amortized time
Since each element is processed at most twice (once when pushed, once when popped), the total time complexity is O(n)
.
Space Complexity: O(n)
in the worst case.
The space is used for:
- The stack
stk
which can contain at mostn
elements when all elements are in increasing order (when traversed in reverse) - The reversed array slice
nums[::-1]
creates a new list that requiresO(n)
additional space - The variable
vk
usesO(1)
space
Therefore, the overall space complexity is O(n)
.
Common Pitfalls
1. Incorrect Initialization of max_k_value
A common mistake is initializing max_k_value
to 0 or some arbitrary number instead of negative infinity. This can cause false positives when the array contains negative numbers.
Incorrect:
max_k_value = 0 # Wrong! Will fail for arrays like [-2, 1, -2]
Correct:
max_k_value = float('-inf') # Ensures no valid comparison until we have a real nums[k]
2. Traversing in Forward Direction
Some might attempt to traverse the array from left to right, which makes the problem significantly more complex and requires tracking multiple variables.
Incorrect Approach:
# Trying to find pattern while going forward
for i in range(len(nums)):
# Complex logic needed to track all three elements simultaneously
Correct Approach:
for current_num in reversed(nums):
# Backwards traversal simplifies the logic
3. Misunderstanding the Stack's Purpose
The stack doesn't store all previously seen elements; it maintains a monotonic decreasing sequence of potential nums[j]
values. A common error is not properly maintaining this invariant.
Incorrect:
# Just pushing everything to stack without maintaining order stack.append(current_num) # Without the popping logic
Correct:
while stack and stack[-1] < current_num: max_k_value = stack.pop() stack.append(current_num)
4. Wrong Comparison Order
When checking for the pattern, using <=
instead of <
or comparing with the wrong variable.
Incorrect:
if current_num <= max_k_value: # Wrong! We need strict inequality return True
Correct:
if current_num < max_k_value: # Strict inequality for valid 132 pattern return True
5. Not Understanding What max_k_value Represents
max_k_value
is not just any popped value—it's specifically the maximum valid nums[k]
that has a corresponding nums[j]
to its right (in the original array). Updating it incorrectly can miss valid patterns.
Incorrect:
while stack and stack[-1] < current_num: stack.pop() # Forgetting to update max_k_value
Correct:
while stack and stack[-1] < current_num: max_k_value = stack.pop() # Must capture the popped value
Solution to Avoid These Pitfalls:
- Always use
float('-inf')
for initialization when dealing with comparison-based algorithms - Carefully trace through the algorithm with small examples to understand the role of each variable
- Remember that the backwards traversal means we're building the pattern from right to left
- Maintain clear variable names that reflect their purpose in the 132 pattern
- Test with edge cases including negative numbers, duplicates, and arrays of length less than 3
What are the most two important steps in writing a depth first search function? (Select 2)
Recommended Readings
Stack Intro Imagine you have a pile of books on your desk If you want to add a new book you place it on top If you want to read a book you take it from the top And if you simply want to see which book is on the top you
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
Monotonic Stack Deque Intro If you'd prefer a video div class responsive iframe iframe src https www youtube com embed Dq_ObZwTY_Q title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div The word monotonic means a list or
Want a Structured Path to Master System Design Too? Don’t Miss This!