1. 程式人生 > 其它 >leetcode300. 最長遞增子序列(動態規劃 貪心 二分)

leetcode300. 最長遞增子序列(動態規劃 貪心 二分)

連結:https://leetcode-cn.com/problems/longest-increasing-subsequence/

題目

給你一個整數陣列 nums ,找到其中最長嚴格遞增子序列的長度。

子序列是由陣列派生而來的序列,刪除(或不刪除)陣列中的元素而不改變其餘元素的順序。例如,[3,6,2,7] 是陣列 [0,3,1,6,2,2,7] 的子序列。

用例

示例 1:

輸入:nums = [10,9,2,5,3,7,101,18]
輸出:4
解釋:最長遞增子序列是 [2,3,7,101],因此長度為 4 。
示例 2:

輸入:nums = [0,1,0,3,2,3]
輸出:4
示例 3:

輸入:nums = [7,7,7,7,7,7,7]
輸出:1

提示:

1 <= nums.length <= 2500
-104 <= nums[i] <= 104

進階:

你可以設計時間複雜度為 O(n2) 的解決方案嗎?
你能將演算法的時間複雜度降低到O(n log(n)) 嗎?

思路

方法一
動態規劃 記錄第一個位置到每一個位置最大距離 O(n^2)
注意*max_element(dp.begin(),dp.end());取陣列最大值
轉移方程dp[i]=max(dp[j]+1,dp[i]) ,nums[i]>nums[j],i>j

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int n=nums.size();
        vector<int>dp(n,1);
        for(int i=0;i<n;++i){
            for(int j=0;j<i;++j){
                if(nums[j]<nums[i]){
                    dp[i]=max(dp[i],dp[j]+1);
                }
            }
        }
        return *max_element(dp.begin(),dp.end());
    }
};

方法二
貪心
維護一個數組,記錄在長度固定的情況下,結尾最小的那個元素的數值
對原序列進行遍歷,將每位元素二分插入 cell 中。
如果 cell 中元素都比它小,將它插到最後
否則,用它覆蓋掉比它大的元素中最小的那個。
思想就是讓 cell 中儲存比較小的元素。這樣,cell 未必是真實的最長上升子序列,但長度是對的。

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int len=1,n=nums.size();
        vector<int>d(n+1,0);
        d[len]=nums[0];
        for(int i=1;i<n;++i){
            if(nums[i]>d[len]){
                d[++len]=nums[i];
            }else{
                int l=1,r=len,pos=0;//說明所有數都比nums[i]大,需要更新d陣列
                while(l<=r){
                    int mid=(l+r)>>1;
                    if(d[mid]<nums[i]){
                        pos =mid;
                        l=mid +1;
                    }else{
                        r=mid-1;
                    }   
                } 
                d[pos+1]=nums[i];
            }
        }
         return len;
    }
};