法1:dp,时间复杂度O(n2),空间复杂度O(n)
定义dp[i]的值代表以nums[i]结尾的最长子序列长度;
初始化,dp[i] 所有元素置1,含义是每个元素都至少可以单独成为子序列,此时长度都为1;
如果nums[i]严格大于[0,i)中的某个数j,那么说明nums[i]就可以在dp[j]的基础上形成一个长度为dp[j]+1的上升子序列;
因此,得到递推关系:if(nums[j]<nums[i]) dp[i]=max(dp[i],dp[j]+1);
//dp[i]的值代表以nums[i]结尾的最长子序列长度
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
if (nums.size() == 0)return 0;
vector<int>dp(nums.size());
//初始化,dp[i] 所有元素置1,含义是每个元素都至少可以单独成为子序列,此时长度都为1
//如果nums[i]严格大于[0,i)中的某个数j,那么说明nums[i]就可以在dp[j]的基础上形成一个长度为dp[j]+1的上升子序列
//因此,得到递推关系:if(nums[j]<nums[i]) dp[i]=max(dp[i],dp[j]+1);
for (int i = 0; i < nums.size(); i++)
dp[i] = 1;
int max_len = 1;
for (int i = 0; i < nums.size(); i++)
{
for (int j = 0; j < i; j++)//j在[0,i)中
{
if (nums[j] < nums[i])
dp[i] = max(dp[i], dp[j] + 1);
}
max_len = max(max_len, dp[i]);
}
return max_len;
}
};
法2:贪心算法(动态规划 + 二分查找)
时间复杂度O(logn),空间复杂度O(n)
建立一个tails数组,res表示当前的上升序列最大长度
初始化:
int res = 1;
tails[0] = nums[0];
思路:每一次来一个新的数num,在tail数组中的[0,res-1]区间找和num相等的数或第一个比num大的数,并记录下标;如果在该区间中找到了,那么将那个数替换成num,如果没有找到,则把num赋值给tail[res],然后res++,体现了贪心的思想。
在tail数组中的[0,res-1]区间找和num相等的数或第一个比num大的数时,可以使用二分法,而二分法的时间复杂度为O(logn);
感慨一下:这个代码改了好久才写出来,真不容易!
这里二分法使用了leetcode第35题的代码。
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
if (nums.size() == 0)return 0;
vector<int>tails(nums.size(), 0);
int res = 1;
tails[0] = nums[0];
for (int i = 1; i < nums.size(); i++)
{
if (BinarySearch(tails, 0, res-1, nums[i]) == res)
res++;
}
return res;
}
int BinarySearch(vector<int>&tails, int left, int right, int target) {
//二分法在升序数组中寻找和target相等的数的下标或第一个比target大的数的下标(如果有)
//这里就是leetcode35题的写法!!!
while (left <= right)
{
int mid = (left + right) / 2;
if (tails[mid] == target)
return mid;
else if (target < tails[mid])
{
right = mid - 1;
}
else
{
left = mid + 1;
}
}
tails[left] = target;//贪心
return left;
}
};