一、题目描述
给定一个未排序的数组,判断这个数组中是否存在长度为 3 的递增子序列。
数学表达式如下:
如果存在这样的 i, j, k, 且满足 0 ≤ i < j < k ≤ n-1,
使得 arr[i] < arr[j] < arr[k] ,返回 true ; 否则返回 false 。
说明: 要求算法的时间复杂度为 O(n),空间复杂度为 O(1) 。
示例 1:
输入: [1,2,3,4,5] 输出: true
示例 2:
输入: [5,4,3,2,1] 输出: false
二、题目分析和代码实现
1、第一种方法——最直接的思路
1)采用动态规划的方法,dp[i]代表以nums[i]为结尾的递增子序列长度
2)dp[i]=max{dp[j]+1},j<i&&nums[j]<nums[i]
3)时间复杂度为n*n
1 class Solution { 2 public: 3 bool increasingTriplet(vector<int>& nums) { 4 int n = nums.size(); 5 if (n < 3)return false; 6 vector<int>dp(n, 1); 7 for (int i = 1; i < n; ++i) { 8 for (int j = 0; j < i; ++j) { 9 if (nums[j] < nums[i]) 10 dp[i] = max(dp[i], dp[j] + 1); 11 } 12 if (dp[i] == 3)return true; 13 } 14 return false; 15 } 16 };
2、符合题目要求的方法——最佳思路,但是比较难想
1)用两个数字m1代表当前最小的数,m2代表遍历到现在第二小的数,m2的位置不一定要在m1之后
2)那么当发现一个数大于m2的数的时候,就直接返回true
3)时间复杂度为n,空间复杂度为常数
1 class Solution { 2 public: 3 bool increasingTriplet(vector<int>& nums) { 4 int n = nums.size(); 5 int m1 = INT_MAX, m2 = INT_MAX; 6 for (int i = 0; i < n; ++i) { 7 if (nums[i] <= m1)m1 = nums[i]; 8 else if (nums[i] <= m2)m2 = nums[i];//要注意等号,也就是遇到相等的也要向后移动 9 else return true; 10 } 11 return false; 12 } 13 };
3)另一种方法——思路很赞
1)Min[i]代表从0到i的最小值,Max[i]代表从i到n的最大值
2)如果nums[i]大于Min[i-1]并且小于Max[i+1],那么就返回真
3)时间复杂度为n,空间复杂度为n
1 class Solution { 2 public: 3 bool increasingTriplet(vector<int>& nums) { 4 int n = nums.size(); 5 if (n < 3)return false; 6 vector<int> Min(n), Max(n); 7 int i; 8 Min[0] = nums[0], Max[n - 1] = nums[n - 1]; 9 for (i = 1; i < n; ++i) { 10 Min[i] = min(Min[i - 1], nums[i]); 11 Max[n - 1 - i] = max(Max[n - i], nums[n - 1 - i]);//注意下标 12 } 13 for (int i = 1; i < n - 1; ++i) { 14 if (nums[i] > Min[i - 1] && nums[i] < Max[i + 1]) 15 return true; 16 } 17 return false; 18 } 19 };