方法一:预处理左右最值数组
思路分析
- 创建两个辅助数组:
minLeft[i]:记录从数组开始到位置i的最小值(包括nums[i])minRight[i]:记录从位置i到数组结束的最大值(包括nums[i])
- 遍历数组,对每个位置i,检查是否存在
minLeft[i] < nums[i] < minRight[i] - 如果存在,则说明找到了递增三元组
代码实现
java
public boolean increasingTriplet(int[] nums){
boolean flag = false;
int[] minLeft = new int[nums.length];
int[] minRight = new int[nums.length];
// 初始化minLeft数组
minLeft[0] = nums[0];
// 初始化minRight数组
minRight[nums.length-1] = nums[nums.length-1];
// 正序遍历,构建minLeft数组
for (int i = 1; i < nums.length; i++) {
minLeft[i] = Math.min(minLeft[i-1], nums[i]);
}
// 倒序遍历,构建minRight数组
for (int i = nums.length-2; i >= 0; i--) {
minRight[i] = Math.max(minRight[i+1], nums[i]);
}
// 检查是否存在递增三元组
for (int i = 0; i < nums.length; i++) {
if (minLeft[i] < nums[i] && nums[i] < minRight[i]) {
flag = true;
}
}
return flag;
}
复杂度分析
- 时间复杂度:O(n),需要三次遍历数组
- 空间复杂度:O(n),需要两个额外的数组
方法二:贪心算法
思路分析
- 维护两个变量:
first:记录当前找到的最小值second:记录当前找到的第二个值(比first大的最小值)
- 遍历数组:
- 如果当前数字小于等于first,更新first
- 如果当前数字大于first但小于等于second,更新second
- 如果当前数字大于second,说明找到了递增三元组,返回true
- 如果遍历完都没找到,返回false
代码实现
java
public boolean increasingTriplet2(int[] nums) {
int first = Integer.MAX_VALUE;
int second = Integer.MAX_VALUE;
for (int i = 0; i < nums.length; i++) {
if (nums[i] <= first){
first = nums[i];
}else if (nums[i] <= second){
second = nums[i];
}else {
return true;
}
}
return false;
}
注意点
- 在遍历过程中可能会出现first在second后面的情况,但是没关系,最终任然能找到正确的三元组,我们以数组{1,4,0,6}为例,
- 初始状态:first = Integer.MAX_VALUE, second = Integer.MAX_VALUE
- num = 1:
1 <= first(Integer.MAX_VALUE) → 更新 first = 1
状态:first = 1, second = Integer.MAX_VALUE - num = 4:
4 > first(1) 且 4 <= second(Integer.MAX_VALUE) → 更新 second = 4
状态:first = 1, second = 4 - num = 0:
0 <= first(1) → 更新 first = 0
状态:first = 0, second = 4 - num = 6:
6 > first(0) 且 6 > second(4) → 满足条件 first < second < num,返回 true - 最终状态:
first = 0 (对应数组索引2的元素)
second = 4 (对应数组索引1的元素)
重要说明:
虽然最终 first 指向的是数组中索引为2的元素0,second 指向的是索引为1的元素4,但算法仍然正确地找到了递增三元组。这是因为:
- 在处理元素4时,我们已经找到了一个递增对:nums[0]=1 < nums[1]=4
- 当遇到元素6时,我们有 nums[1]=4 < nums[3]=6
- 虽然 first 的值被更新为0,但 second=4 仍然表示在某个较小值之后出现的较大值
- 所以 first=0 < second=4 < num=6 形成了一个有效的递增三元组
- 注意判断条件是小于等于,而不是小于
例如测试数组{1,1,1,1,1,},如果使用的是小于,则会返回true,实际上正确结果应该是false。
- i = 0, num = 1:1 < Integer.MAX_VALUE → first = 1
- i = 1, num = 1:1 < 1 → 不满足,进入 else if → 1 < Integer.MAX_VALUE → second = 1
- i = 2, num = 1:1 < 1 → 不满足,1 < 1 → 不满足,进入 else → return true
复杂度分析
- 间复杂度:O(n),只需一次遍历数组
- 空间复杂度:O(1),只使用了常数级额外空间
对比总结
| 特征 | 方法一(预处理数组) | 方法二(贪心算法) |
|---|---|---|
| 时间复杂度 | O(n) | O(n) |
| 空间复杂度 | O(n) | O(1) |
| 代码复杂度 | 较复杂 | 较简单 |
| 实现难度 | 中等 | 中等 |
| 优势 | 思路直观,容易理解 | 空间效率高 |