1. 程式人生 > >[LeetCode] Increasing Triplet Subsequence 遞增的三元子序列

[LeetCode] Increasing Triplet Subsequence 遞增的三元子序列

Given an unsorted array return whether an increasing subsequence of length 3 exists or not in the array.

Formally the function should:

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.

Your algorithm should run in O(n) time complexity and O(1

) space complexity.

Examples:
Given [1, 2, 3, 4, 5],
return true.

Given [5, 4, 3, 2, 1],
return false.

Credits:
Special thanks to @DjangoUnchained for adding this problem and creating all test cases.

這道題讓我們求一個無序陣列中是否有任意三個數字是遞增關係的,我最先相處的方法是用一個dp陣列,dp[i]表示在i位置之前小於等於nums[i]的數字的個數(包括其本身),我們初始化dp陣列都為1,然後我們開始遍歷原陣列,對當前數字nums[i],我們遍歷其之前的所有數字,如果之前某個數字nums[j]小於nums[i],那麼我們更新dp[i] = max(dp[i], dp[j] + 1),如果此時dp[i]到3了,則返回true,若遍歷完成,則返回false,參見程式碼如下:

解法一:

// Dumped, brute force
class Solution {
public:
    bool increasingTriplet(vector<int>& nums) {
        vector<int> dp(nums.size(), 1);
        for (int i = 0; i < nums.size(); ++i) {
            for (int j = 0; j < i; ++j) {
                if (nums[j] < nums[i]) {
                    dp[i] 
= max(dp[i], dp[j] + 1); if (dp[i] >= 3) return true; } } } return false; } };

但是題目中要求我們O(n)的時間複雜度和O(1)的空間複雜度,上面的那種方法一條都沒滿足,所以白寫了。我們下面來看滿足題意的方法,這個思路是使用兩個指標m1和m2,初始化為整型最大值,我們遍歷陣列,如果m1大於等於當前數字,則將當前數字賦給m1;如果m1小於當前數字且m2大於等於當前數字,那麼將當前數字賦給m2,一旦m2被更新了,說明一定會有一個數小於m2,那麼我們就成功的組成了一個長度為2的遞增子序列,所以我們一旦遍歷到比m2還大的數,我們直接返回ture。如果我們遇到比m1小的數,還是要更新m1,有可能的話也要更新m2為更小的值,畢竟m2的值越小,能組成長度為3的遞增序列的可能性越大,參見程式碼如下:

解法二:

class Solution {
public:
    bool increasingTriplet(vector<int>& nums) {
        int m1 = INT_MAX, m2 = INT_MAX;
        for (auto a : nums) {
            if (m1 >= a) m1 = a;
            else if (m2 >= a) m2 = a;
            else return true;
        }
        return false;
    }
};

如果覺得上面的解法不容易想出來,那麼如果能想出下面這種解法,估計面試官也會為你點贊。這種方法的雖然不滿足常數空間的要求,但是作為對暴力搜尋的優化,也是一種非常好的解題思路。這個解法的思路是建立兩個陣列,forward陣列和backward陣列,其中forward[i]表示[0, i]之間最小的數,backward[i]表示[i, n-1]之間最大的數,那麼對於任意一個位置i,如果滿足 forward[i] < nums[i] < backward[i],則表示這個遞增三元子序列存在,舉個例子來看吧,比如:

nums:        8  3  5  1  6

foward:      8  3  3  1  1

backward:  8  6  6  6  6

我們發現數字5滿足forward[i] < nums[i] < backward[i],所以三元子序列存在。

解法三:

class Solution {
public:
    bool increasingTriplet(vector<int>& nums) {
        if (nums.size() < 3) return false;
        int n = nums.size();
        vector<int> f(n, nums[0]), b(n, nums.back());
        for (int i = 1; i < n; ++i) {
            f[i] = min(f[i - 1], nums[i]);
        }
        for (int i = n - 2; i >= 0; --i) {
            b[i] = max(b[i + 1], nums[i]);
        }
        for (int i = 0; i < n; ++i) {
            if (nums[i] > f[i] && nums[i] < b[i]) return true;
        }
        return false;
    }
};

參考資料: