一、题目
给你一个整数数组 nums ,判断这个数组中是否存在长度为 3 的递增子序列。
如果存在这样的三元组下标 (i, j, k) 且满足 i < j < k ,使得 nums[i] < nums[j] < nums[k] ,返回 true ;否则,返回 false 。
示例 1:
输入:nums = [1,2,3,4,5]
输出:true
解释:任何 i < j < k 的三元组都满足题意
示例 2:
输入:nums = [5,4,3,2,1]
输出:false
解释:不存在满足题意的三元组
示例 3:
输入:nums = [2,1,5,0,4,6]
输出:true
解释:三元组 (3, 4, 5) 满足题意,因为 nums[3] == 0 < nums[4] == 4 < nums[5] == 6
提示:
- 1 <= nums.length <= 105
- -231 <= nums[i] <= 231 - 1
进阶:你能实现时间复杂度为 O(n) ,空间复杂度为 O(1) 的解决方案吗?
二、解决
1、动态规划
思路:
每次寻找比当前数小的元素个数,待完善……
代码:
class Solution {
public boolean increasingTriplet(int[] nums) {
int n = nums.length;
int[] dp = new int[n];
Arrays.fill(dp, 1);
for (int i = 0; i < n; ++i) {
for (int j = 0; j < i; ++j) {
if (nums[j] < nums[i]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
if (dp[i] >= 3) return true;
}
}
return false;
}
}
时间复杂度: O ( n 2 ) O(n^2) O(n2)
时间复杂度: O ( n ) O(n) O(n)
2、前后遍历
思路:
定义:
forward[i]:从前向后遍历,保存[0, i]之间最小元素值。
backward[i]:从后向前遍历,保存[i, n - 1]间最大元素值。
例如:
nums[i]: 8 3 5 1 6
forwa[i]: 8 3 3 1 1
backw[i]:8 6 6 6 6
代码:
class Solution {
public boolean increasingTriplet(int[] nums) {
int n = nums.length;
if (n < 3) return false;
int[] f = new int[n]; f[0] = nums[0];
int[] b = new int[n]; b[n-1] = nums[n-1];
for (int i = 1; i < n; i++) {
f[i] = Math.min(f[i - 1], nums[i]);
}
for (int i = n - 2; i >= 0; i--) {
b[i] = Math.max(b[i + 1], nums[i]);
}
for (int i = 0; i < n; i++) {
if (f[i] < nums[i] && nums[i] < b[i]) return true;
}
return false;
}
}
时间复杂度: O ( n ) O(n) O(n)
时间复杂度: O ( n ) O(n) O(n)
3、双指针
思路:
疑问:当已经找到长度为 2 的递增序列,又来 1 个比small
小的数字,为什么可以直接替换small
,small
和mid
并不是按索引递增啊?
说明:假如当前small=3和 mid=5,这时来了个 1,如果不将 small 替换为 1,那当下一个数字是 2,后面再接一个 3 时,就没办法发现 [1,2,3] 递增数组了!也就是说,替换最小值是为了后续更好地更新中间值。
另外,即使更新了 small ,这个 small 在 mid 后面,没严格遵守递增顺序,但它隐含真相是,有一个数 num
,small < num && num < mid
, 最小值出现在mid
前。因此,当后续出现比 mid 大的值的时候,可以通过当前 small
和 mid
推断,的确存在着长度为 3 的递增序列。 所以,这样替换不会干扰后续计算!
代码:
class Solution {
public boolean increasingTriplet(int[] nums) {
int small = Integer.MAX_VALUE, mid = Integer.MAX_VALUE;
for (int num : nums) {
if (num <= small) small = num;
else if (num <= mid) mid = num;
else return true;
}
return false;
}
}
时间复杂度: O ( n ) O(n) O(n)
时间复杂度: O ( 1 ) O(1) O(1)
三、参考
1、C++ 线性时间复杂度详细解析,打败 98%
2、【334. 递增的三元子序列】三种思路(动态规划+双指针+前后遍历)
3、python3 扩展到一般情况,找是否存在长度为n的递增子序列
4、My way to approach such a problem. How to think about it? Explanation of my think flow.
5、My java solution works for not just triplet