问题:
求给定数组中,最长递增子序列的长度。
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 is 4. 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?
解法:
解法一:DP(动态规划) Time:O(N^2)
dp[i]:截止到nums[i],包含nums[i]的最长递增子序列的长度。
状态转移方程:dp[i] = max(dp[j]+1) (所有j满足:0~i,nums[j]<nums[i])
边界:dp[0] = 1(nums[0]只有自己一个数字,最长递增子序列长度,也为 1 )
代码参考:
1 class Solution { 2 public: 3 //dp[i]:until nums[i](include nums[i]), the length of the longest increasing Subsequence 4 //dp[i] = max(dp[i], dp[j]+1) (nums[j]<nums[i]) 5 //base:dp[0]=1 6 int lengthOfLIS(vector<int>& nums) { 7 int res = 0; 8 vector<int> dp(nums.size(), 1); 9 for(int i=0; i<nums.size(); i++) { 10 for(int j=0; j<i; j++) { 11 if(nums[j]<nums[i]) { 12 dp[i] = max(dp[i], dp[j]+1); 13 } 14 } 15 res = max(res, dp[i]); 16 } 17 return res; 18 } 19 };
解法二:二分查找(Binary Search) Time:O(NlogN)
类似如下游戏:顺序抽取牌,若当前牌<顺序(i:0~res)排列的第 i 堆牌顶top,则将当前牌放入牌堆 i 的牌顶。(上面为牌底,下面为牌顶)
若直到 第res堆牌的牌顶top,都<当前牌,那么新建一个牌堆,将当前牌推入新牌堆中。
那么,这一列牌堆有如下特点:
i:0->res 牌顶为 递增数列
每一堆牌,从牌顶到牌底(由下到上), 递增
由于每次新建牌堆,都是,所有牌顶都<当前牌,的情况下,才做的。
因此最长递增序列长度,则为 牌堆的堆数。 至少所有牌底可以构成一个满足的子序列。
那么,对于本问题,我们可以模拟该游戏,最终返回堆数res即可。
这里,在拿到当前牌,去插入牌堆的时候,对递增数列的牌顶(下),可使用二分查找。
⚠️ 注意:本题中,[2,2]的结果为 1,则说明只有完全>的数列,才为所求。
因此在上述游戏中,若找到相同的数字,则仍然合并在同一堆中,不进行新堆的创建,
因此二分查找,要找到第一个 >=当前牌 的牌堆,进行插入。
代码参考:
class Solution { public: int lengthOfLIS(vector<int>& nums) { int res = 0; vector<int> top(nums.size()); for(int i=0; i<nums.size(); i++) { int poker = nums[i]; int l = 0, r = res, mid; while(l<r) { mid = l + (r-l)/2; if(top[mid]>=poker) { r = mid; }else{ l = mid+1; } } if(l==res) res++; top[l] = poker; } return res; } };