给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
示例 2:
输入:nums = [0,1,0,3,2,3] 输出:4
示例 3:
输入:nums = [7,7,7,7,7,7,7] 输出:1
基本思路:对于子序列最值问题,子序列尾部元素是否被选中是一个很重要的隐含状态,它可以帮忙递推下一个的状态,动态规划,dp[i]表示以i为终点的最长上升长度。
- 对于一个严格递增的子序列,当前元素是否能续上,主要是比较当前元素和改子序列的尾部元素的大小关系
- 所以可以设计一个dp[i],表示以i结尾的最长上升序列,dp[i]=max(dp[j]+1,dp[i]) if dp[i]>dp[j]
int lengthOfLIS(vector<int>& nums) {
if(nums.size()==0)
return 0;
int maxLen=1;
vector<int> dp(nums.size(),1);
for(int i=1;i<dp.size();i++){
for(int j=0;j<i;j++){
if(nums[j]<nums[i]){
dp[i]=max(dp[i],dp[j]+1);
}
}
maxLen=max(maxLen,dp[i]);
}
return maxLen;
}
基本思路:贪心算法,如果要让上升序列尽可能的长,那么就尽量让序列上升的尽可能地缓慢,即尾部元素尽可能的小,据此,设置一个tail数据,tail[i]表示长度为i+1的序列的末尾的最小值,end为有效tail的末端。
- 初始值,tail[0]=nums[0]
- 若nums[i]>tail[end],则tail[++end]=nums[i]
- 否则,二分查找插入tail数组
有插入过程可知tail数组一定是有序的(0~end之间),且严格递增。
证明:
反证法,设i<j,但是tail[j]<=tail[i],对于tail[i]一定存在a0<a1...<ai;对于tail[j]一定存在b0<b1<...<bi<...bj;有定义可知ai>=bj>bi,那么必然存在b0<b1<...<bi<ai,这与tail[i]的定义不符,ai不是最小的元素。
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
if(nums.size()==0)
return 0;
vector<int> tail(nums.size(),0);
tail[0]=nums[0];
int end=0;
for(int i=1;i<nums.size();i++){
if(nums[i]>tail[end]){
tail[++end]=nums[i];
}
else{
int left=0;
int right=end;
while(left<right){
int mid=left+(right-left)/2;
if(tail[mid]<nums[i]){
left=mid+1;
}
else{
right=mid;
}
}
tail[left]=nums[i];
}
}
end++;
return end;
}
};
同上,另外一种表示
int lengthOfLIS(vector<int>& nums) {
vector<int> dp(nums.size(),1);
int ans=1;
for(int i=1;i<nums.size();i++){
for(int j=0;j<i;j++){
if(nums[i]>nums[j]){
dp[i]=max(dp[j]+1,dp[i]);
}
}
ans=max(ans,dp[i]);
}
return ans;
}
类似题目: