题目描述
给定一个无序数组,请判断数组中是否存在长度为3的递增子序列。即:
是否存在 i,j,k,
满足 arr[i]<arr[j]<arr[k], 其中 0≤i<j<k≤n−1。
你的算法只能用 O(n) 的时间以及额外 O(1) 的空间。
样例1
输入:[1, 2, 3, 4, 5],
输出:true.
样例2
输入:[5, 4, 3, 2, 1],
输出:false.
算法
(线性扫描) O(n)
类似于求最长上升子序列问题的优化思路。
从前往后遍历整个数组,遍历时维护2个值,分别表示:
- 上升子序列长度为1时,该数的最小值;
- 上升子序列长度为2时,第二个数的最小值;
为了方便叙述,我们把记录的第一个数记为 q1,记录的第二个数记为 q2,则 q1<q2。否则,如果 q1≥q2,则由于 q2 是某个长度为2的上升子序列的第二个数,所以该序列的第一个数小于 q2,从而小于 q1,与 q1 是最小数矛盾。
然后分情况讨论,对于当前数 x:
- 如果 x<q1,则把 q1 更新成 x。同时由于 q1 是当前最小数,所以长度为2的序列的第一个数大于等于 q1,从而大于 x,所以不能更新 q2;
- 否则,如果 x≥q1 且 x<q2,则 x 可以接在 q1 后面,组成一个长度为2的上升序列,所以可以将 q2 更新成 x;
- 否则,如果 x>q2,则说明找到了长度为3的上升子序列。
时间复杂度分析:整个数组只被遍历一次,所以时间复杂度是 O(n),遍历时只额外记录2个变量,所以额外的空间复杂度是 O(1)。
C++ 代码
class Solution {
public:
bool increasingTriplet(vector<int>& nums) {
vector<int>q(2);
int tt = -1;
for (int x : nums)
{
if (tt < 0) q[ ++ tt] = x;
else
{
int k = 0;
while (k <= tt && q[k] < x) k ++ ;
if (k <= tt) q[k] = x;
else
{
if (tt < 1) q[ ++ tt] = x;
else return true;
}
}
}
return false;
}
};
y总讲的太复杂了。不需要参考最长上升子序列的做法,回归这道题的本质,其实是 维护一个最小值和一个次小值即可。详细看我的题解:LeetCode 334. 递增的三元子序列
这是算是模拟一个队列吗?
算,不过很特殊。本题做法其实是896. 最长上升子序列 II的简化版。
这个做法真的是妙不可言!
这其实是最长上升子序列问题的简化版hh