题目:
我的思路:
要求时间O(n),想贪心
对于第一个数来说,如果它位于一个下坡序列,那么它一定取坡底那个数
然后,第二个数从一个新的下坡开始,在下坡的过程中,它不能像第一个数那样一直下去,它要保证大于第一个数,所以它的取值有两种情况,一是下到最后一个大于第一个数的位置,二是在发生一之前遇到比上坡。第二种情况显然直接返回 true。
接着讨论第三个数,此时第二个数必定是以第一种情况结尾的,因此这是第三个数紧接着第二个数后面开始寻找有没有比第二个数大的,如果有则返回 true,如果没有,那么需要重新开始寻找。
第一个数如何重新确定开始下标 ? 显然第一个数取第一个下坡的坡底是没有解的,那么就从第二个坡的坡底重新开始寻找符合条件的三元组
至此能通过95%的案例,超时的原因是在寻找第三个数时每次我们都需要遍历接下来的整个数组,但事实上我们只需要直到 有没有比第二个数大的就可以了,所以需要事先从末尾遍历数组,记录每个下标在它之后的最大值是多少。
这样能以时间和空间都为O(n),通过测试。
优化:我陷入了一种固定思维,对于三元组我们总认为它是下标从小到大的三个数,这就导致在寻找这三个数的过程中(定义了i,j,k为三个数的下标)我们始终保持i < j < k ,限制了更贪心的做法。
当我们开始讨论第三个数的时候,显然是从 j + 1开始的,并且nums[j + 1] < nums[j] < nums[j - 1],所以我们完全可以令 i = j + 1,因为第三个数的条件是要大于第二个数,和第一个数没有直接关系。当继续寻找第三个数的过程中,如果发现了比第二数大的就直接返回true,如果比第二个数小比第一个数大,就令 j 为这个新的下标(这时我们就可发现,这种情况就是上面我的思路中第二次寻找开始的情况),如果比第一个数小,就令 i 为这个新的下标 。
这样就实现了时间O(n),空间O(1)
自己的(3ms)
class Solution {
public boolean increasingTriplet(int[] nums) {
int[] max = new int[nums.length];
int m = 0;
for(int i = nums.length - 1; i >= 0; i--){
m = Math.max(m,nums[i]);
max[i] = m;
}
int i = 0;
while(true){
while(i + 1 < nums.length && nums[i + 1] <= nums[i])
i++;
int j = i + 1;
while(j + 1 < nums.length && nums[j + 1] <= nums[j] && nums[j + 1] > nums[i])
j++;
if(i + 1 == nums.length || j + 1 == nums.length)
return false;
int k = j + 1;
if(max[k] > nums[j])
return true;
i = j + 1;
}
}
}
题解(0ms)
class Solution {
public boolean increasingTriplet(int[] nums) {
int n = nums.length;
if (n < 3) {
return false;
}
int first = nums[0], second = Integer.MAX_VALUE;
for (int i = 1; i < n; i++) {
int num = nums[i];
if (num > second) {
return true;
} else if (num > first) {
second = num;
} else {
first = num;
}
}
return false;
}
}