题目
标题和出处
标题:递增的三元子序列
难度
6 级
题目描述
要求
给你一个整数数组 nums \texttt{nums} nums,如果存在这样的三元组下标 (i, j, k) \texttt{(i, j, k)} (i, j, k) 满足 i < j < k \texttt{i < j < k} i < j < k 和 nums[i] < nums[j] < nums[k] \texttt{nums[i] < nums[j] < nums[k]} nums[i] < nums[j] < nums[k],返回 true \texttt{true} true;否则,返回 false \texttt{false} false。
示例
示例 1:
输入:
nums
=
[1,2,3,4,5]
\texttt{nums = [1,2,3,4,5]}
nums = [1,2,3,4,5]
输出:
true
\texttt{true}
true
解释:任何
i
<
j
<
k
\texttt{i < j < k}
i < j < k 的三元组都满足题意。
示例 2:
输入:
nums
=
[5,4,3,2,1]
\texttt{nums = [5,4,3,2,1]}
nums = [5,4,3,2,1]
输出:
false
\texttt{false}
false
解释:不存在满足题意的三元组。
示例 3:
输入:
nums
=
[2,1,5,0,4,6]
\texttt{nums = [2,1,5,0,4,6]}
nums = [2,1,5,0,4,6]
输出:
true
\texttt{true}
true
解释:三元组
(3,
4,
5)
\texttt{(3, 4, 5)}
(3, 4, 5) 满足题意,因为
nums[3]
=
0
<
nums[4]
=
4
<
nums[5]
=
6
\texttt{nums[3] = 0 < nums[4] = 4 < nums[5] = 6}
nums[3] = 0 < nums[4] = 4 < nums[5] = 6。
数据范围
- 1 ≤ nums.length ≤ 10 5 \texttt{1} \le \texttt{nums.length} \le \texttt{10}^\texttt{5} 1≤nums.length≤105
- -2 31 ≤ nums[i] ≤ 2 31 − 1 \texttt{-2}^\texttt{31} \le \texttt{nums[i]} \le \texttt{2}^\texttt{31}-\texttt{1} -231≤nums[i]≤231−1
进阶
你能实现时间复杂度为 O(n) \texttt{O(n)} O(n),空间复杂度为 O(1) \texttt{O(1)} O(1) 的解决方案吗?
解法
思路和算法
这道题中,数组 nums \textit{nums} nums 的长度最大为 5 × 1 0 5 5 \times 10^5 5×105,暴力解法会超出时间限制,必须使用复杂度较低的解法。
由于题目要寻找递增的三元子序列,只有当数组的长度大于等于 3 3 3 时,才存在三元子序列,因此当数组的长度小于 3 3 3 时,直接返回 false \text{false} false 即可。
当数组的长度大于等于 3 3 3 时,只需要遍历数组一次,使用 O ( n ) O(n) O(n) 时间和 O ( 1 ) O(1) O(1) 的空间即可判断是否存在递增的三元子序列。维护两个变量 firstNum \textit{firstNum} firstNum 和 secondNum \textit{secondNum} secondNum 分别存储递增三元子序列的第一个数和第二个数,任何时候都满足 firstNum < secondNum \textit{firstNum}<\textit{secondNum} firstNum<secondNum,如果遍历过程中遇到一个大于 secondNum \textit{secondNum} secondNum 的元素,即找到了一个递增三元子序列。
具体做法是,初始化 firstNum = nums [ 0 ] \textit{firstNum}=\textit{nums}[0] firstNum=nums[0], secondNum = + ∞ \textit{secondNum}=+\infty secondNum=+∞(代码中使用整型的最大值表示 + ∞ +\infty +∞)。由于 nums [ 0 ] \textit{nums}[0] nums[0] 已经赋值给 firstNum \textit{firstNum} firstNum,因此从下标 1 1 1 开始遍历数组 nums \textit{nums} nums。对于遍历到的元素 num \textit{num} num,进行如下操作:
-
如果 num > secondNum \textit{num}>\textit{secondNum} num>secondNum,则 ( firstNum , secondNum , num ) (\textit{firstNum},\textit{secondNum},\textit{num}) (firstNum,secondNum,num) 即为同时满足下标递增和元素值递增的递增三元子序列,返回 true \text{true} true;
-
如果 firstNum < num ≤ secondNum \textit{firstNum}<\textit{num} \le \textit{secondNum} firstNum<num≤secondNum,则如果在 num \textit{num} num 的后面存在 num 2 \textit{num}_2 num2 满足 num 2 > num \textit{num}_2>\textit{num} num2>num,则 ( firstNum , num , num 2 ) (\textit{firstNum},\textit{num},\textit{num}_2) (firstNum,num,num2) 即为一个递增三元子序列,由于 num ≤ secondNum \textit{num} \le \textit{secondNum} num≤secondNum,因此将 num \textit{num} num 的值赋给 secondNum \textit{secondNum} secondNum,只要在 num \textit{num} num 的后面找到一个元素大于更新后的 secondNum \textit{secondNum} secondNum,即找到一个递增三元子序列;
-
如果 num ≤ firstNum \textit{num} \le \textit{firstNum} num≤firstNum,则将 num \textit{num} num 的值赋给 firstNum \textit{firstNum} firstNum。
上述做法中,如果找到一个递增三元子序列,则三个元素值一定是递增顺序,那么下标是否也满足递增顺序?答案是肯定的。理由如下:
-
当找到 num > secondNum \textit{num}>\textit{secondNum} num>secondNum 时, num \textit{num} num 一定在 secondNum \textit{secondNum} secondNum 的后面,即 num \textit{num} num 的下标一定大于 secondNum \textit{secondNum} secondNum 的下标;
-
当 secondNum \textit{secondNum} secondNum 被更新时,更新后的 secondNum \textit{secondNum} secondNum 一定在 firstNum \textit{firstNum} firstNum 的后面,即 secondNum \textit{secondNum} secondNum 的下标一定大于 firstNum \textit{firstNum} firstNum 的下标;
-
当 firstNum \textit{firstNum} firstNum 被更新时,虽然更新后的 firstNum \textit{firstNum} firstNum 在 secondNum \textit{secondNum} secondNum 的后面,但是更新前的 firstNum ′ \textit{firstNum}' firstNum′ 满足 firstNum ′ < secondNum \textit{firstNum}'<\textit{secondNum} firstNum′<secondNum 且 firstNum ′ \textit{firstNum}' firstNum′ 在 secondNum \textit{secondNum} secondNum 的前面,即 firstNum ′ \textit{firstNum}' firstNum′ 的下标小于 secondNum \textit{secondNum} secondNum 的下标。
因此,虽然没有记录三个元素的具体下标值,但是可以保证三个元素的下标满足递增顺序。
代码
class Solution {
public boolean increasingTriplet(int[] nums) {
int length = nums.length;
if (length < 3) {
return false;
}
int firstNum = nums[0], secondNum = Integer.MAX_VALUE;
for (int i = 1; i < length; i++) {
int num = nums[i];
if (num > secondNum) {
return true;
} else if (num > firstNum) {
secondNum = num;
} else {
firstNum = num;
}
}
return false;
}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums \textit{nums} nums 的长度。遍历数组 nums \textit{nums} nums 一次,遍历过程中维护 firstNum \textit{firstNum} firstNum 和 secondNum \textit{secondNum} secondNum 的值,对于每个位置,维护两个变量的时间复杂度是 O ( 1 ) O(1) O(1),因此总时间复杂度是 O ( n ) O(n) O(n)。
-
空间复杂度: O ( 1 ) O(1) O(1)。