题目描述:
给你一个整数数组 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 <= 5 * 105
-231 <= nums[i] <= 231 - 1
思路一:动态规划
这道题和“求除自身外其他元素的乘积”的思路有些相同,我们可以把满足 nums[i] < nums[j] < nums[k]
的num[j]当作中间元素,num[j]将数组分为左右两部分,其中左侧子数组的最小值满足 nums[i] < nums[j],右侧子数组的最大值满足
nums[j] < nums[k]
那么如何求得以中间元素为界,左右两个子数组的最小和最大值呢?
以左侧最小值为例,
- 先初始化一个数组,长度为nums.length,并且元素全赋值为0。
- 接下来先把nums[0]看作是最小值赋值给leftMin[0]
- 遍历leftMin数组,比较最小值数组leftMin中的第i-1个元素与nums数组中第i个元素的大小,把小的赋值给最小值数组元素leftMin[i]
- 一直循环,就能得到第i个元素左侧最小值
右侧最大值同理,不过在遍历时,需要逆向遍历数组
具体实现:
- 初始化:创建两个数组
leftMin
和rightMax
,分别用于存储每个位置左侧的最小值和右侧的最大值。 - 填充
leftMin
:遍历数组,更新每个位置左侧的最小值。 - 填充
rightMax
:逆向遍历数组,更新每个位置右侧的最大值。 - 检查递增三元组:遍历数组,检查是否存在满足条件的三元组,即当前元素大于其左侧的最小值且小于其右侧的最大值。
代码如下:
var increasingTriplet = function(nums) {
// 获取数组的长度
const n = nums.length;
// 如果数组长度小于3,则不可能存在递增的三元组,直接返回false
if (n < 3) {
return false;
}
// 初始化一个数组leftMin,用于存储每个位置左侧的最小值
const leftMin = new Array(n).fill(0);
leftMin[0] = nums[0];
// 遍历数组,更新leftMin数组
for (let i = 1; i < n; i++) {
leftMin[i] = Math.min(leftMin[i - 1], nums[i]);
}
// 初始化一个数组rightMax,用于存储每个位置右侧的最大值
const rightMax = new Array(n).fill(0);
rightMax[n - 1] = nums[n - 1];
// 逆向遍历数组,更新rightMax数组
for (let i = n - 2; i >= 0; i--) {
rightMax[i] = Math.max(rightMax[i + 1], nums[i]);
}
// 遍历数组,检查是否存在满足条件的三元组
for (let i = 1; i < n - 1; i++) {
// 如果当前元素大于其左侧的最小值且小于其右侧的最大值,则找到了一个递增的三元组
if (nums[i] > leftMin[i - 1] && nums[i] < rightMax[i + 1]) {
return true;
}
}
// 如果没有找到符合条件的三元组,返回false
return false;
};
思路二:利用贪心算法
这里官方给出的题解思路较难理解,通过查询其他人的帖子发现一个容易理解的思路
- 首先,我们假定 first < second一定满足,此时我们只要找到 third
- 当 third > second,即满足条件,直接返回true
- 如果 third < second,但是third > first,那就把second指向third,然后继续遍历找third
- 如果third比first还小,那就把first指向third,然后继续遍历找third(这样的话first会跑到second的后边,但是不要紧,因为在second的前边,老first还是满足的)
具体实现:
- 初始化:设置两个变量
first
和second
,分别表示数组中的第一个元素和第二个元素。 - 遍历数组:对于每个元素,如果它大于
second
,则找到了递增的三元组,返回true
。- 如果当前元素大于
first
但小于second
,则更新second
为当前元素。 - 如果当前元素小于等于
first
,则更新first
为当前元素。
- 如果当前元素大于
- 结果:如果遍历完数组后没有找到递增的三元组,返回
false
代码如下:
/**
* @param {number[]} nums
* @return {boolean}
*/
var increasingTriplet = function (nums) {
const length = nums.length;
if (length < 3) {
return false;
}
let first = nums[0], second = Number.MAX_VALUE;
for (let i = 1; i < length; i++) {
const num = nums[i];
if (num > second) {
return true;
} else if (num > first) {
second = num;
} else {
first = num;
}
}
return false;
};