题目描述
给你一个整数数组 nums ,判断这个数组中是否存在长度为 3 的递增子序列。
如果存在这样的三元组下标 (i, j, k) 且满足 i < j < k ,使得 nums[i] < nums[j] < nums[k] ,返回 true ;否则,返回 false 。
算法示例
输入:nums = [1,2,3,4,5]
输出:true
解释:任何 i < j < k 的三元组都满足题意
输入:nums = [5,4,3,2,1]
输出:false
解释:不存在满足题意的三元组
输入:nums = [2,1,5,0,4,6]
输出:true
解释:三元组 (3, 4, 5) 满足题意,因为 nums[3] == 0 < nums[4] == 4 < nums[5] == 6
提示:
1 <= nums.length <= 5 * 105-231 <= nums[i] <= 231 - 1
方法一:暴力【超时】
- 看数据范围O(n^2)会超时
class Solution {
public boolean increasingTriplet(int[] nums) {
int n = nums.length;
int[] f = new int[n];
Arrays.fill(f, 1);
for(int i = 1; i < n; i++) {
for(int j = 0; j < i; j++) {
if(nums[i] > nums[j]) {
f[i] = Math.max(f[j] + 1, f[i]);
if(f[i] >= 3) return true;
}
}
}
return false;
}
}
方法二:LIS&二分
-
我们考虑维护一个列表 f,其中每个元素 f[k]的值代表 长度为 k+1 的子序列尾部元素的值。
-
举例:7,8,9,1,2,3,4,5序列,遍历前3个元素的时候,f=[7,8,9]
而遍历到1的时候,需要在f进行二分找到适合1的位置,得到f=[1,8,9],此时说明[7,8,9,1]长度为1的子序列中最小结尾元素为1
依此类推,得到最终的f=[1,2,3,4,5],该结果就是最长递增子序列(LIS)
-
f列表一定是递增的,即当尽可能使每个子序列尾部元素值最小的前提下,子序列越长,其序列尾部元素值一定更大。
class Solution {
public boolean increasingTriplet(int[] nums) {
int n = nums.length;
// 表示长度为k + 1的最长上升子序列的最小结尾元素
int[] f = new int[n + 1];
Arrays.fill(f, Integer.MAX_VALUE);
int ans = 0;
for(int i = 0; i < n; i++) {
int l = 1, r = i + 1;
while (l <= r) {
int mid = l + (r - l) /2;
if (f[mid] >= nums[i]) r = mid - 1;
else l = mid + 1;
}
// l左侧全部小于nums[i],r右侧全部大于等于nums[i]
// 因此直接覆盖掉大于等于nums[i]的第一个元素即可
f[l] = nums[i];
// 此时l下标就代表递增子序列的长度
ans = Math.max(ans, l);
}
System.out.println(Arrays.toString(f));
return ans >= 3;
}
}