A clear explanation of Increasing Triplet Subsequence using greedy tracking of two minimum values.
Problem Restatement
We are given an integer array nums.
Return true if there exists an increasing subsequence of length 3.
That means we need indices:
i < j < ksuch that:
nums[i] < nums[j] < nums[k]The elements do not need to be consecutive. They only need to appear in increasing index order.
The problem asks for:
| Requirement | Value |
|---|---|
| Time complexity | O(n) |
| Extra space | O(1) |
Input and Output
| Item | Meaning |
|---|---|
| Input | Integer array nums |
| Output | true if an increasing triplet exists |
| Required order | i < j < k |
| Required values | nums[i] < nums[j] < nums[k] |
Function shape:
def increasingTriplet(nums: list[int]) -> bool:
...Examples
Example 1:
Input: nums = [1,2,3,4,5]
Output: trueOne valid triplet is:
1 < 2 < 3Example 2:
Input: nums = [5,4,3,2,1]
Output: falseThe sequence is strictly decreasing, so no increasing triplet exists.
Example 3:
Input: nums = [2,1,5,0,4,6]
Output: trueOne valid triplet is:
0 < 4 < 6First Thought: Try All Triplets
A direct approach is:
- Choose index
i. - Choose index
j > i. - Choose index
k > j. - Check whether:
nums[i] < nums[j] < nums[k]This checks every possible triplet.
O(n^3)This is far too slow.
We need something linear.
Key Insight
We do not need to remember all previous numbers.
We only need to track:
| Variable | Meaning |
|---|---|
first | Smallest value seen so far |
second | Smallest possible second value of an increasing pair |
The goal is to eventually find a number larger than second.
If we do, then we already have:
first < second < currentwhich forms an increasing triplet.
The greedy idea is:
- Keep
firstas small as possible. - Keep
secondas small as possible afterfirst.
Smaller values make it easier to find a larger third value later.
Algorithm
Initialize:
first = infinity
second = infinityThen scan the array from left to right.
For each number x:
- If
x <= first, updatefirst. - Else if
x <= second, updatesecond. - Else:
- We found:
first < second < x- Return
True.
If the loop ends without finding such a value, return False.
Walkthrough
Use:
nums = [2,1,5,0,4,6]Start:
first = inf
second = infRead 2:
first = 2Read 1:
first = 1Smaller first value is better.
Read 5:
second = 5Now we have an increasing pair:
1 < 5Read 0:
first = 0This improves the smallest value.
Read 4:
second = 4Now we have a better increasing pair:
0 < 4Read 6.
Since:
6 > secondwe found:
0 < 4 < 6Return True.
Why Updating first Does Not Break Things
A common confusion is:
What if second was chosen before first changed?Example:
[2,1,5,0,4,6]Initially:
first = 1
second = 5Later:
first = 0Now second = 5 came before 0.
Does that break the logic?
No.
Because later we update:
second = 4The algorithm always maintains the best possible increasing pair seen so far.
The pair represented by (first, second) always appears in valid order inside the scanned prefix.
Correctness
The algorithm maintains these invariants while scanning the array:
| Invariant | Meaning |
|---|---|
first | Smallest value seen so far |
second | Smallest value greater than first seen so far |
When processing a value x:
If:
x <= firstthen replacing first with x is always safe, because a smaller first value makes future triplets easier to form.
If:
first < x <= secondthen replacing second with x is safe because we now have a smaller increasing pair:
first < xA smaller second value increases the chance of finding a future third value larger than it.
If:
x > secondthen we already have:
first < second < xwith indices in increasing order because first and second were formed during earlier iterations.
Therefore, an increasing triplet subsequence exists.
If the algorithm finishes without reaching the third case, then no increasing triplet exists in the array.
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(n) | One scan through the array |
| Space | O(1) | Only two variables are stored |
Implementation
class Solution:
def increasingTriplet(self, nums: list[int]) -> bool:
first = float("inf")
second = float("inf")
for x in nums:
if x <= first:
first = x
elif x <= second:
second = x
else:
return True
return FalseCode Explanation
Initialize the two tracking values:
first = float("inf")
second = float("inf")They represent the smallest values found so far.
For each number:
for x in nums:If x is smaller than or equal to first, update first:
if x <= first:
first = xOtherwise, if x improves the second value:
elif x <= second:
second = xNow we have a better increasing pair.
Otherwise:
else:
return TrueThis means:
x > second > firstSo an increasing triplet exists.
If the loop ends, no such triplet was found.
Testing
def run_tests():
s = Solution()
assert s.increasingTriplet([1, 2, 3, 4, 5]) == True
assert s.increasingTriplet([5, 4, 3, 2, 1]) == False
assert s.increasingTriplet([2, 1, 5, 0, 4, 6]) == True
assert s.increasingTriplet([1, 1, 1, 1]) == False
assert s.increasingTriplet([1, 2]) == False
assert s.increasingTriplet([1, 2, 2, 3]) == True
assert s.increasingTriplet([20, 100, 10, 12, 5, 13]) == True
print("all tests passed")
run_tests()Test meaning:
| Test | Why |
|---|---|
| Increasing array | Simple valid triplet |
| Decreasing array | No valid triplet |
| Mixed values | Standard greedy example |
| Duplicate values | Strict inequality matters |
| Too short | Need at least three values |
| Repeated middle value | Handles duplicates correctly |
| Late triplet | Triplet appears after resets |