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 };