Given an unsorted array of integers, find the length of longest increasing subsequence.
Example:
Input:[10,9,2,5,3,7,101,18]
Output: 4 Explanation: The longest increasing subsequence is[2,3,7,101]
, therefore the length is4
.
Note:
- There may be more than one LIS combination, it is only necessary for you to return the length.
- Your algorithm should run in O(n2) complexity.
Follow up: Could you improve it to O(n log n) time complexity?
分析:求解數組最長遞增子序列(嚴格遞增).
思路一:暴力求解。遞歸。
最簡單的方法是找出所有的遞增子序列,然后返回最長遞增子序列的長度。為此,我們使用遞歸函數lengthOfLIS返回當前元素(對應於curpos)以后可能出現的LIS長度(包括當前元素)。在每個函數調用中,我們考慮兩種情況:
1) 當前元素比LIS中包含的前一個元素大。在這種情況下,我們可以在LIS中包含當前元素。因此,我們通過包含它來求出LIS的長度。此外,我們還通過在LIS中不包含當前元素來確定LIS的長度。因此,當前函數調用返回的值是兩個長度中的最大值。
2) 當前元素小於LIS中包含的前一個元素。在這種情況下,我們不能在LIS中包含當前元素。因此,我們僅通過在LIS中不包含當前元素來確定LIS的長度。
1 class Solution { 2 public: 3 int lengthOfLIS(vector<int>& nums) { 4 int len = nums.size(); 5 return lengthOfLIS(nums, INT_MIN, 0, len); 6 } 7 private: 8 int lengthOfLIS(vector<int> nums, int prev, int curpos, int len) { 9 if (curpos == len) { 10 return 0; 11 } 12 int taken = 0; 13 if (nums[curpos] > prev) { 14 taken = 1 + lengthOfLIS(nums, nums[curpos], curpos + 1, len); 15 } 16 int notaken = lengthOfLIS(nums, prev, curpos + 1, len); 17 return max(taken, notaken); 18 } 19 };
時間復雜度O(2n)
思路二:記憶化的遞歸
在前一種方法中,許多遞歸調用必須一次又一次地使用相同的參數。通過將特定調用的結果存儲在二維記憶數組memo中,可以消除這種冗余。memo[i][j]表示可以使用nums[i]作為LIS中包含/不包含的前一個元素,而nums[j]作為LIS中包含/不包含的當前元素,來表示LIS的長度。這里,nums表示給定的數組。
1 class Solution { 2 public: 3 int lengthOfLIS(vector<int>& nums) { 4 5 int len = nums.size(); 6 vector<vector<int> > memo(len + 1, vector<int>(len, -1)); 7 //memset(memo, -1, sizeof(memo)); 8 return lengthOfLIS(nums, -1, 0, len, memo); 9 } 10 private: 11 int lengthOfLIS(vector<int> nums, int previndex, int curpos, int len, vector<vector<int> > &memo) { 12 if (curpos == len) { 13 return 0; 14 } 15 if (memo[previndex + 1][curpos] >= 0) { 16 return memo[previndex + 1][curpos]; 17 } 18 int taken = 0; 19 if (previndex < 0 || nums[curpos] > nums[previndex]) { 20 taken = 1 + lengthOfLIS(nums, curpos, curpos + 1, len, memo); 21 } 22 int notaken = lengthOfLIS(nums, previndex, curpos + 1, len, memo); 23 memo[previndex + 1][curpos] = max(taken, notaken); 24 return memo[previndex + 1][curpos]; 25 } 26 };
時間復雜度O(n2), 空間復雜度 O(n2)
思路三:動態規划,dp[i]表示以下標為i的數字為結尾的最長遞增子序列長度。
1 class Solution { 2 public: 3 int lengthOfLIS(vector<int>& nums) { 4 int len = nums.size(); 5 vector<int> dp(len, 0); 6 int maxn = 0; 7 for (int i = 0; i < len; i++) { 8 dp[i] = 1; 9 for (int j = 0; j < i; j++) { 10 if (nums[i] > nums[j]) 11 dp[i] = max(dp[i], dp[j] + 1); 12 } 13 maxn = max(maxn, dp[i]); 14 } 15 return maxn; 16 } 17 };
時間復雜度:O(n2), 空間復雜度O(n)
思路四:動規+二分
1 class Solution { 2 public: 3 int lengthOfLIS(vector<int>& nums) { 4 int len = nums.size(); 5 if (len == 0) 6 return 0; 7 int *dp = new int[len]; 8 int length = 0; 9 for (int i = 0; i < len; i++) { 10 int ind = BinarySearch(dp, 0, length, nums[i]); 11 dp[ind] = nums[i]; 12 if (ind == length) 13 length++; 14 } 15 return length; 16 } 17 private: 18 //返回數組中第一個大於target的數的下標 19 int BinarySearch(int *p, int left, int right, int target) { 20 while (left < right) { 21 int mid = ((right - left) >> 1) + left; 22 if (p[mid] < target) 23 left = mid + 1; 24 else 25 right = mid; 26 } 27 return right; 28 } 29 };