问题描述:
Given an unsorted array of integers, find the length of longest increasing subsequence.
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?
给定一个未经排序的整数数组,寻找最长递增子序列的长度。
问题求解:
方法一:动态规划。时间复杂度为O(n^2)。
状态转移方程:dp[i] = max{dp[j]+1,dp[i]}, j
class Solution {
public://动态规划。时间复杂度为O(n^2)。
int lengthOfLIS(vector<int>& nums) {
int n=nums.size();
if(n <= 1) return n;
vector<int> dp(n, 1);
for(int i=1;i//dp[i]表示LIS的长度。nums[i]作为LIS的最后一个元素。
for(int j=0;jif(nums[i] > nums[j])
{//满足递增
dp[i]=max(dp[i], dp[j]+1);//利用状态转移方程
}
}
}
int res=0;
for(int i=0;i//求得最大的dp[i]
res = max(res, dp[i]);
}
return res;
}
};
方法二:贪心法+二分搜索!!!时间复杂度O(n * log n),空间复杂度O(n)!!!
设dp[i]表示以i为结尾的最长递增子序列的长度。考虑两个数a[x]和a[y],x < y且a[x] < a[y]
,且dp[x]=dp[y]
,当a[t]要选择时,到底取哪一个构成最优的呢?显然选取a[x]更有潜力,因为可能存在a[x],这样a[t]可以获得更优的值。在这里给我们一个启示,当dp[t]一样时,尽量选择更小的a[x]。
在1,3,5,2,8,4,6
这个例子中,当到6时,一共可以有四种不同长度,且保证该升序序列在同长度升序序列中末尾最小的升序序列。
(1)
1,(2)
1,3,(4)
1,3,5,(6)
注:每个序列的末尾数字加括号。
这些序列都是有可能成为最长递增子序列的候选者。
tail[i]表示长度为i的递增序列末尾的数字(如上1,2,4,6存在tail中)。tail[]数组性质:tail[0] < tail[1] < … < tail[n] !!!
每次读入一个新元素时,可按照以下规则更新这些序列:
1)nums[i]比所有递增序列的末尾都小,则长度为1的序列更新为这个更小的末尾。
如果读入数字0,则更新序列为:
(0)
1,(2)
1,3,(4)
1,3,5,(6)
2)nums[i]比所有序列的末尾都大,则直接将nums[i]加到后面。
如果读入数字9,更新序列为:
(1)
1,(2)
1,3,(4)
1,3,5,(6)
1,3,5,6,(9)
3)在中间,则更新那个末尾数字刚好大于等于nums[i]的那个序列,nums[i]替换其末尾数字。该情况下,利用二分搜索寻找。
如果读入数字3,更新序列为:
(1)
1,(2)
1,3,(3)
1,3,5,(6)
代码:
class Solution {
public://
int lengthOfLIS(vector<int>& nums) {
int n=nums.size();
if(n <= 1) return n;
//tail[i]表示长度为i的递增序列末尾的数字
//tail[]数组性质:tail[0]
vector<int> tail(n);//初始化为n个值为0的元素
//1.len为当前最长的递增序列长度(为方便操作将len减1,从0开始,最后再加上1)
int len=0;
tail[0]=nums[0];
//2.每次读入一个新元素nums[i]
for(int i=1;i//遍历nums[]中的数
if(nums[i] < tail[0])
{//(1)nums[i]比所有递增序列的末尾都小,则长度为1的序列更新为这个更小的末尾。
tail[0]=nums[i];
}
else if(nums[i] > tail[len])
{//(2)nums[i]比所有序列的末尾都大,则直接将nums[i]加到后面
tail[++len]=nums[i];
}
else
{//(3)在中间,则更新那个末尾数字刚好大于等于nums[i]的那个序列,nums[i]替换其末尾数字
tail[biSearch(tail, 0, len, nums[i])]=nums[i];
}
}
return len+1;
}
int biSearch(vector<int>& tail, int low, int high, int target)
{//由于tail数组是有序的,故可二分查找其中元素
while(low <= high)//不能是low
{//当low=high时还要进行一次循环!!!
//此时mid=low=high.若tail[mid]
int mid = low + (high-low)/2;
if(tail[mid] == target) return mid;
else if(tail[mid] > target)
{
high=mid-1;
}
else
{
low=mid+1;
}
}
return low;
}
};