300. (LIS最长递增子序列 动态规划)
For example,
Given [10, 9, 2, 5, 3, 7, 101, 18]
,
The longest increasing subsequence is [2, 3, 7, 101]
, therefore the length is 4
. Note that 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?
class Solution: def lengthOfLIS(self, nums: List[int]) -> int: #dp[i] 表示以 nums[i] 这个数结尾的最长递增子序列的长度 dp = [1] * len(nums) for i in range(len(nums)): for j in range(i): if nums[i] > nums[j]: dp[i] = max(dp[i],dp[j]+1) return max(dp)
dp[i]
表示以 nums[i]
这个数结尾的最长递增子序列的长度
class Solution { public: int lengthOfLIS(vector<int>& nums) { if (nums.size() <= 1) return nums.size(); int dp[nums.size()]; //dp[i]表示i之前包括i的最长上升子序列。 for(int i = 0;i < nums.size();++i) { dp[i] = 1; //每一个i,对应的dp[i](即最长上升子序列)起始大小至少都是是1. } int result = 0; for (int i = 1; i < nums.size(); i++) { for (int j = 0; j < i; j++) { if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1); } if (dp[i] > result) result = dp[i]; // 取长的子序列 } return result; } };
长度为N的数组记为A={a0 a1 a2...an-1};
记A的前i个字符构成的前缀串为Ai= a0 a 1a2...ai-1,以ai结尾的最长递增
子序列记做Li,其长度记为a[i];
假定已经计算得到了a[0,1…,i-1],如何计算a[i]呢?
根据定义, Li必须以ai结尾,如果将ai缀到L0 L1…… Li-1的后面,是否
允许呢?
如果aj<ai,则可以将ai缀到Lj的后面,并且使得Lj的长度变长。
从而:a[i]={max(a(j))+1, 0 ≤j≤i-1且a[j]≤a[i] }
需要遍历在i之前的所有位置j,找出满足条件a[j]≤a[i]的a[j];
计算得到a[0…n-1]后,遍历所有的a[i],找出最大值即为最大递增子序列
的长度。
时间复杂度为O(N2)。
思考:如何求最大递增子序列本身?
记录前驱
1 class Solution { 2 public int lengthOfLIS(int[] a) { 3 if(a.length==0) return 0; 4 int[] longs = new int[a.length]; 5 for(int i = 0;i<a.length;i++) 6 longs[i] = 1; 7 int max = longs[0]; 8 for(int i = 1;i < a.length;i++){ 9 for(int j = 0;j <= i-1;j++) 10 if(a[j]<a[i]) 11 if(longs[i]<longs[j]+1) 12 longs[i] = longs[j]+1; 13 //如果求序列本身,在这里记录前驱 14 15 if(max<longs[i]) 16 max = longs[i]; 17 } 18 return max; 19 }
暴力:
class Solution { public: int lengthOfLIS(vector<int>& nums) { return solve(nums, 0, INT_MIN); } int solve(vector<int>& nums, int i, int prev) { if(i >= size(nums)) return 0; // cant pick any more elements int take = 0, dontTake = solve(nums, i + 1, prev); // try skipping the current element if(nums[i] > prev) take = 1 + solve(nums, i + 1, nums[i]); // or pick it if it is greater than previous picked element return max(take, dontTake); // return whichever choice gives max LIS } };
记忆化 :
class Solution { public: vector<vector<int>> dp; int lengthOfLIS(vector<int>& nums) { dp.resize(size(nums), vector<int>(1+size(nums), -1)); // dp[i][j] denotes max LIS starting from i when nums[j] is previous picked element return solve(nums, 0, -1); } int solve(vector<int>& nums, int i, int prev_i) { if(i >= size(nums)) return 0; if(dp[i][prev_i+1] != -1) return dp[i][prev_i+1]; int take = 0, dontTake = solve(nums, i + 1, prev_i); if(prev_i == -1 || nums[i] > nums[prev_i]) take = 1 + solve(nums, i + 1, i); // try picking current element if no previous element is chosen or current > nums[prev_i] return dp[i][prev_i+1] = max(take, dontTake); } };
降维:
class Solution { public: vector<int> dp; int lengthOfLIS(vector<int>& nums) { dp.resize(size(nums)+1, -1); return solve(nums, 0, -1); } int solve(vector<int>& nums, int i, int prev_i) { if(i >= size(nums)) return 0; if(dp[prev_i+1] != -1) return dp[prev_i+1]; int take = 0, dontTake = solve(nums, i + 1, prev_i); if(prev_i == -1 || nums[i] > nums[prev_i]) take = 1 + solve(nums, i + 1, i); return dp[prev_i+1] = max(take, dontTake); } };