132 Pattern
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.
- n == nums.length
- 1 <= n <= 2 * 10⁵
- -10⁹ <= nums[i] <= 10⁹
nums = [1,2,3,4]falsenums = [3,1,4,2]truenums = [-1,3,2,0]trueA 132 pattern is a sequence of three numbers—Small (1), Large (3), and Medium (2)—where the Large number sits in the middle and the Medium number is on the right. This is hard to find because it's not a simple upward or downward slope; it's a "Sandwich" where a peak is trapped between two smaller values.
A naive approach would be to check every possible triplet $(i, j, k)$, which takes O(N^3) time. We could optimize this to O(N^2) by fixing the middle "Large" number and searching for the smallest value to its left and any valid value to its right. However, to reach linear time, we need a way to track these dependencies simultaneously.
The secret to solving this in a single pass is to Search Backward. By scanning from right to left, we can resolve the relationship between the "Large" (3) and the "Medium" (2) first. We maintain a variable s2 to track the largest "Medium" candidate we've seen so far that has a larger "Large" neighbor to its left.
The strategy works as follows:
- Reverse Scan: We move through the array from right to left. Every number we see is a potential "Large" (3) candidate for the numbers to its left.
- Identify the Medium (s2): We use a monotonic stack to store potential "2" candidates. Every time the current number is larger than the top of the stack, we've found a new "Large" (3). We "pop" the stack and update s2 to be that popped value. By always taking the largest possible popped value, we give our future "Small" (1) the best chance to succeed.
- Find the Small (1): As we continue left, if we ever find a number strictly smaller than our current s2, the pattern is complete. We have our Small (1), and we know there is a Large (3) and a Medium (2) already resolved to its right.
# Monotonic Stack (O(N) Time, O(N) Space)
stack = []
s2 = float('-inf') # The "Medium" candidate
# Search backward to resolve 3 and 2 dependencies first
for i in range(len(nums) - 1, -1, -1):
# If current num is "Small" enough to be less than our "Medium"
if nums[i] < s2:
return True
# If current num is a potential "Large" (3), update "Medium" (2)
while stack and nums[i] > stack[-1]:
s2 = stack.pop()
# Push current num as a potential candidate
stack.append(nums[i])
return FalseBy using a stack in reverse, we effectively "buffer" potential candidates until we can prove they are part of a 132 sequence. Every element enters and leaves the stack at most once, ensuring linear time.
132 Pattern Strategy
1-3-2 logic
- R to L Scan: Find the 'Peak' and 'Middle' before reaching the 'Start'.
- The Stack: Maintains candidates for the peak "3".
Peak-Valley-Peak
By preserving the largest popped value as s3, we satisfy 1 < 2 < 3. If any element to the left of our peak is smaller than s3, we've found it!