給定一個無序的整數數組,找到其中最長上升子序列的長度。
示例:
輸入: [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是單調遞增的。