334. Increasing Triplet Subsequence
不定期更新leetcode解题java答案。
采用pick one的方式选择题目。
题意为检测一个无序数组中是否存在三个递增序列(可不连续)。
Return true if there exists i, j, k
such that arr[i] < arr[j] < arr[k] given 0 ≤ i < j < k ≤ n-1 else return false.
要求尽量时间复杂度在O(n),空间复杂度在O(1)。
主要考虑几种情况,将可能出现的三个递增序列依次命名为leftNum,midNum,rightNum,在遍历检测数组时,一共有两种可能出现的情况(除了在检测数组第一个数字前leftNum是空值,其他过程中leftNum均为存在的值):
1、我们检测新数字之前只有leftNum存在,前面不存在递增序列,midNum还无法添加;
2、前面已有递增序列,midNum已经添加完,只求能有更大值填入rightNum,最后返回true。
由于有两种情况,我们需要一个控制变量来标志midNum是否已经被填入。我们依次分析这两种情况:
情况1,如果出现数比leftNum大,则填入midNum,进入情况2;否则替换leftNum,仍处于情况1。
情况2,如果出现数比midNum大,则返回true。如果比leftNum大比midNum小,则将新数字替换为midNum;然而,当数字比leftNum小,问题则出现,由此可看出我们仍需要一个变量minNum(请仔细看清minNum和midNum)来存储数组最小值。
这时我们重新将上述问题分析一遍,过程变成了这样:
情况1,如果出现数比minNum大,则填入midNum,进入情况2;否则替换minNum,仍处于情况1(这里minNum与leftNum实际是等价的)。
情况2,如果出现数比midNum大,则返回true。如果比minNum小,则替换minNum(逻辑是这样,因为minNum记录的就是数组已经遍历的最小值);如果出现数在minNum和midNum之间,那么无论与leftNum大小关系如何,我们应该将minNum替换为leftNum,将出现数替换为midNum,这样是利用贪心法取局部最优解,后面的情况依旧处于情况2,继续进行遍历。这里我们发现经过我们的分析,所有情况都没有使用到leftNum这个变量,因此没有必要去定义这个变量。
当然,如果遍历完整个数组依旧没有返回true,则返回false。时间复杂度为O(n),空间复杂度为O(1)。
具体代码如下:
1 public class Solution { 2 public boolean increasingTriplet(int[] nums) { 3 if(nums.length < 3) 4 return false; 5 int minNum = nums[0]/*, leftNum = nums[0]*/, midNum = 0; 6 boolean getMidNum = false; 7 for(int i = 1; i < nums.length; i++){ 8 if(getMidNum){ 9 if(nums[i] <= midNum){ 10 if(nums[i] <= minNum) 11 minNum = nums[i]; 12 else{ 13 // leftNum = minNum; //actually need not, no use for leftNum 14 midNum = nums[i]; 15 } 16 }else
17 return true; 18 }else{ 19 if(nums[i] > minNum){ 20 getMidNum = true; 21 midNum = nums[i]; 22 }else 23 minNum = nums[i]; 24 } 25 } 26 return false; 27 } 28 }
局部代码9~17行,如用这种方式替换,运行时间在leetcode上会从2ms变成1ms,可能和测试用例有关,具体代码如下:
1 if(nums[i] < midNum){ 2 if(nums[i] < minNum) 3 minNum = nums[i]; 4 else if(nums[i] > minNum){ 5 // leftNum = minNum; //actually need not, no use for leftNum 6 midNum = nums[i]; 7 } 8 }else if(nums[i] > midNum) 9 return true;