題目
給定一個未排序的數組,判斷這個數組中是否存在長度爲 3 的遞增子序列。
數學表達式如下:
如果存在這樣的 i, j, k, 且滿足 0 ≤ i < j < k ≤ n-1,
使得 arr[i] < arr[j] < arr[k],返回 true ; 否則返回 false 。
說明: 要求算法的時間複雜度爲 O(n),空間複雜度爲 O(1) 。
示例 1:
輸入: [1,2,3,4,5]
輸出: true
示例 2:
輸入: [5,4,3,2,1]
輸出: false
思路
首先,新建兩個變量 small 和 mid ,分別用來保存題目要我們求的長度爲 3 的遞增子序列的最小值和中間值。
接着,我們遍歷數組,每遇到一個數字,我們將它和 small 和 mid 相比,
- 若小於等於 small ,則替換 small;
- 否則,若小於等於 mid,則替換 mid;
- 否則,若大於 mid,則說明我們找到了長度爲 3 的遞增數組!(已確認了第一、二個數,第三個數一但確定,則大勢定矣)
求解過程中有個問題:
當已經找到了長度爲 2 的遞增序列,這時又來了一個比 small 還小的數字,爲什麼可以直接替換 small 呢,這樣 small 和 mid 在原數組中並不是按照索引遞增的關係呀?
例如: 檢測完了第一二號元素,此時small = 3, mid = 5, 這時檢測到了一個 “2”, 也就是說要用三號“2”將一號“1”替換,此時【small = 三號“2” mid = 二號“5”】,與題意不符。
爲了解釋這個問題,我們假如當前的 small 和 mid 爲 [3, 5],這時又來了個 1。假如我們不將 small 替換爲 1,那麼,當下一個數字是 2,後面再接上一個 3 的時候,我們就沒有辦法發現這個 [1,2,3] 的遞增數組了!也就是說,我們替換最小值,是爲了後續能夠更好地更新中間值!
且,即使我們更新了 small ,這個 small 在 mid 後面,沒有嚴格遵守遞增順序,但它卻可以證明,有一個比 small 大比 mid 小的值出現在 mid 之前。因此,當後續出現比 mid 大的值的時候,我們一樣可以通過當前 small 和 mid 推斷的確存在着長度爲 3 的遞增序列。 所以,這樣的替換並不會干擾我們後續的計算!
前兩段有點拗口,但是注意一句話可以迎刃而解:
有一個比 small
大比 mid
小的值出現在 mid 之前
這是保證程序原理不出錯的關鍵。
代碼如下:
class Solution {
public:
bool increasingTriplet(vector<int>& nums) {
int len = nums.size();
if (len < 3) return false;
int small = INT_MAX, mid = INT_MAX;
for (auto num : nums) { // 定義num變量,將它的指針順次指向nums數組的每個元素(關於auto,百度一下,你就知道)
//接下來三行是程序主體
if(num <= small) small = num;
else if(num <= mid) mid = num;
else if(num > mid) return true; //發現了第三個數,大功告成
}
return false;
}
};
點個贊再走唄~