LeetCode 300. 最长上升子序列(Longest Increasing Subsequence)
题目描述
给出一个无序的整形数组,找到最长上升子序列的长度。
例如,
给出 [10, 9, 2, 5, 3, 7, 101, 18]
,
最长的上升子序列是 [2, 3, 7, 101]
,因此它的长度是4
。因为可能会有超过一种的最长上升子序列的组合,因此你只需要输出对应的长度即可。
解题思路
用动态规划思想,考虑用一个数组dp记录到当前数字为止,可能的最长上升子序列长度,注意并不一定是当前子序列的解。这样最后返回dp数组的长度即可。具体以上述数组为例:
- 首先把10加入到dp中,此时最长上升子序列长度为1
- 下一个数字是9,它比dp中仅有的数字10要小,可知以9为子序列首数字的可能长度要比10长,因此用9替换10
- 同样把2替换dp中仅有的数字9
- 加入5时,因为5比2大,所以可以组成最长上升子序列,因此把5加入到2之后
- 当前数字3比dp中第二个数字5要小,考虑到之后可能出现的上升序列可能小于5,因此用3替换5
- 加入7时,因为7比dp中最后一个数字3大,所以可以组成最长上升子序列,因此把7加入到3之后
- 同样加入101到dp
- 加入18时,按上述规则用18替换101,最后dp数组为[2,3,7,18],因此最长上升子序列长度为4
通过以上顺序,可以总结出dp数组变化规则:
- 若当前数字大于dp中最后一个数字,则直接插入到最后
- 找到dp数组中第一个大于当前数字的数,并替换为当前数字
- 遍历完数组后,dp数组的大小即为最长上升子序列的长度
其中查找dp数组中第一个大于当前数字的数时,可用二分查找降低时间复杂度,这样此解法的总时间复杂度为Ο(nlogn)
代码
1 class Solution { 2 public: 3 int lengthOfLIS(vector<int>& nums) { 4 int l=nums.size(); 5 vector<int> dp; 6 if(l==0) 7 return 0; 8 dp.push_back(nums[0]); 9 for(int i=1;i<l;i++){ 10 biReplace(dp,nums[i]); 11 } 12 return dp.size(); 13 } 14 void biReplace(vector<int>& dp, int x){ 15 int f=0,l=dp.size()-1; 16 if(x>dp[l]){ 17 dp.push_back(x); 18 return; 19 } 20 int m=(f+l)/2; 21 while(dp[m]!=x){ 22 if(dp[m]>x) 23 l=m-1; 24 else f=m+1; 25 if(f>l){ 26 m=f; 27 break; 28 } 29 m=(f+l)/2; 30 } 31 dp[m]=x; 32 } 33 };