題目描述
給你一個整數數組 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 中存在一個下標i 滿足1≤i<n?1,使得在nums[i] 的左邊存在一個元素小于nums[i] 且在nums[i] 的右邊存在一個元素大于nums[i],則數組nums 中存在遞增的三元子序列
nums[i] 的左邊存在一個元素小于nums[i] 等價于 i的左側最小值小于nums[i]
nums[i] 的右邊存在一個元素大于nums[i] 等價于 i的右側最大值大于nums[i]
創建兩個長度為n 的數組leftMin 和 rightMax對于0<=i<n,leftMin[i]表示num[0]到nums[i]的最小值,rightMax[i]表示nums[i]到nums[n-1]的最大值,
對于任意i,如果存在nums[i] > leftMin[i-1] 且 nums[i] < rightMax[i+1],則存在遞增三元子序列
數組leftMin 的計算方式如下:
leftMin[0] = nums[0]
從左到右遍歷數組nums leftMin[i] = min(leftMin[i-1],nums[I])
數組rightMax 的計算方式如下:
rightMax[n-1] = nums[n-1]
從右往左遍歷數組nums rightMax[i] = max(rightMax[i+1],nums[I])
// OC
+ (BOOL)increasingTriplet1:(NSArray *)nums {
int n = (int)nums.count;
if (n < 3) {
return NO;
}
int leftMin[n],rightMax[n];
// 從左向右 遍歷 計算leftMin[i]
leftMin[0] = [nums[0] intValue];
for (int i=1; i<nums.count; i++) {
leftMin[i] = MIN(leftMin[i-1], [nums[i] intValue]);
}
// 從右向左 遍歷 計算rightMax[i]
rightMax[n-1] = [nums[n-1] intValue];
for (int i=n-2; i>=0; i--) {
rightMax[i] = MAX(rightMax[i+1], [nums[i] intValue]);
}
for (int i=1; i<n-1; i++) {
if (leftMin[i-1] < [nums[i] intValue] && [nums[i] intValue] < rightMax[i+1]) {
return YES;
}
}
return NO;
}
// Swift
static public func increasingTriplet1(_ nums:[Int]) -> Bool {
let n = nums.count;
if n < 3 {
return false
}
var leftMin = Array(repeating: 0, count: n)
var rightMax = Array(repeating: 0, count: n)
// 從左向右 遍歷 計算leftMin[i]
leftMin[0] = nums[0]
for i in 1..<nums.count {
leftMin[i] = min(leftMin[i-1], nums[i])
}
// 從右向左 遍歷 計算rightMax[i]
rightMax[n-1] = nums[n-1]
for i in (0..<(n-1)).reversed() {
rightMax[i] = max(rightMax[i+1], nums[i])
}
for i in 1..<n-1 {
if leftMin[i-1] < nums[i] && nums[i] < rightMax[i+1] {
return true
}
}
return false
}
思路2: 貪心算法
從左到右遍歷數組nums,遍歷過程中維護兩個變量first 和second,分別表示遞增的三元子序列中的第一個數和第二個數,任何時候都有first<second
初始時,first=nums[0],second=+∞。對于1≤i<n,當遍歷到下標i時,令num=nums[i],進行如下操作:
如果num>second,則找到了一個遞增的三元子序列,返回 true
否則,如果num>first,則將second 的值更新為num;
否則,將first 的值更新為num。
如果遍歷結束時沒有找到遞增的三元子序列,返回false
貪心思想是:為了找到遞增的三元子序列,first 和 second 應該盡可能地小,此時找到遞增的三元子序列的可能性更大
假設(first,second,num) 是一個遞增的三元子序列,如果存在second’ 滿足first<second’<second 且second’ 的下標位于first 的下標和num 的下標之間,則
(first,second’,num) 也是一個遞增的三元子序列。但是當(first,second’,num) 是遞增的三元子序列時,由于num 不一定大于second,因此
(first,second,num) 未必是遞增的三元子序列。由此可見,為了使找到遞增的三元子序列的可能性更大,三元子序列的第二個數應該盡可能地小,將second’ 作為三元子序列的第二個數優于將second 作為三元子序列的第二個數。
同理可得,三元子序列的第一個數也應該盡可能地小。
如果遍歷過程中遇到的所有元素都大于first,則當遇到num>second 時,first 一定出現在second 的前面,second 一定出現在 num 的前面,(first,second,num) 即為遞增的三元子序列。
如果遍歷過程中遇到小于first 的元素,則會用該元素更新first,雖然更新后的first 出現在second 的后面,但是在second 的前面一定存在一個元素first’ 小于second,因此當遇到num>second 時,(first’,second,num) 即為遞增的三元子序列。
根據上述分析可知,當遇到num>second 時,一定存在一個遞增的三元子序列,該三元子序列的第二個數和第三個數分別是second 和num,因此返回true
// OC
+ (BOOL)increasingTriplet2:(NSArray *)nums {
int n = (int)nums.count;
if (n < 3) {
return NO;
}
int first = [nums[0] intValue];
int second = INT_MAX;
for (int i=1; i<n; i++) {
int num = [nums[i] intValue];
if (num > second) {
return YES;
}else if (num > first){
second = num;
}else{
first = num;
}
}
return NO;
}
// Swift
static public func increasingTriplet2(_ nums:[Int]) -> Bool {
let n = nums.count;
if n < 3 {
return false
}
var first = nums[0]
var second = Int.max
for i in 1..<n {
let num = nums[i]
if num > second {
return true
}else if num > first {
second = num
}else{
first = num
}
}
return false
}
參考:
https://leetcode-cn.com/leetbook/read/top-interview-questions-medium/xvvuqg/