動態規划-最長上升子序列(LIS)


給定一個無序的整數數組,找到其中最長上升子序列的長度。

示例:

輸入: [10,9,2,5,3,7,101,18]
輸出: 4
解釋: 最長的上升子序列是 [2,3,7,101],它的長度是 4。
說明:

可能會有多種最長上升子序列的組合,你只需要輸出對應的長度即可。
你算法的時間復雜度應該為 O(n2) 。
進階: 你能將算法的時間復雜度降低到 O(n log n) 嗎?

 

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int n = nums.size();
        if(n == 0) return 0;
        if(n == 1) return 1;
        int len = 1;
        vector<int> dp;
        dp.push_back(nums[0]);
        for(int i = 1; i < n; i++){
            if(nums[i] > dp[len-1]){
                dp.push_back(nums[i]);
                len++;
            }
            else{
                int k = search(dp, nums[i]);
                dp[k+1] = nums[i];
            }
        }
        return len;
    }
    
    int search(vector<int> &arr, int x){
        int low = 0;
        int high = arr.size()-1;
        int k = (low+high)/2;
        while(!(arr[k] < x && x <= arr[k+1]) && high > low){
            if(arr[k+1] < x){
                low = k+1;
                k = (low+high)/2;
            }
            else if(x <= arr[k]){
                high = k-1;
                k = (low+high)/2;
            }
        }
        if(high > low) return k;
        else if(x <= arr[k]) return -1;
        else return 0;
    }
};

動態轉移方程為: dp[i] = max(dp[j]) + 1, 其中 0<= j <= i-1, dp[i] 表示以 nums[i] 結尾的上升子序列的長度

根據DP方程,很顯然你每次計算以 nums[i] 為結尾的上升子序列長度的時候,都要從 0 到 i-1遍歷一下dp[],這樣下時間復雜度就是O(n2)了

其實可以用到貪心的思想降低時間復雜度:

想要上升子序列最長,那就是每次上升的時候增幅小一點

所以可以設置一個數組,其中記錄每個長度下最后一個元素的最小值。

則每次計算 dp[i] 的時候,就可以不用遍歷一遍dp了

而只需要對 dp 進行二分查找,這么一來,遍歷dp的時間復雜度就由 O(n) 降到了 O(log n),整體的時間復雜度降為 O(n log n)

為什么可以對dp進行二分查找呢?因為dp是單調遞增的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM