动态规划
记录每一个以i为结尾的LIS
dp[i]与在其之前的所有数比较,小于就加入它,判断所有加入后的序列的最大值即为以该点结尾的最大值
状态转换方程
dp[i] = max(dp[i], dp[j] + 1) (其中: j from i-1 downto 0)
初始化
dp[0] = 1;
输出:
max(dp[i]) i from 0 to sequence.size() - 1
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int len = nums.size();
vector<int> dp(len, 1);
int rst = 0;
for(int i=0; i<len; ++i)
{
for(int j=0; j<i; ++j)
{
if(nums[i] > nums[j] && dp[i] < dp[j]+1) dp[i] = dp[j]+1;
}
rst = max(rst, dp[i]);
}
return rst;
}
};
- 时间复杂度: O(N^2)
- 空间复杂度: O(N)
贪心 + 二分查找
可以用贪心法更快得到答案
- 内层循环主要耗时在寻找比当前元素小上
- 末尾元素越小越有可能加入新元素
- 维护一个dp数组,其长度为当前LIS长度
- nums[i] 与 dp[dp.size() - 1 ] 比较
- 大于,加入
- 小于找到第一个不大于它的数替换掉 (二分查找)
- 初始化
- dp[0] = 0; 用于统一操作
- 输出
- dp.size() - 1 去除一开始输入的0
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int len = nums.size();
vector<int>dp_pro(len, 1);
if(len == 0) return 0;
dp_pro[0] = nums[0];
int end = 0;
for(int i=1; i<len; ++i)
{
if(nums[i] > dp_pro[end]) dp_pro[++end] = nums[i];
else
{
int search[2] = {0,end};
while(search[0] < search[1])
{
int mid = search[0] + ((search[1] - search[0]) >> 1);
if(nums[i] > dp_pro[mid]) search[0] = mid+1;
else search[1] = mid;
}
dp_pro[search[0]] = nums[i];
}
}
++end;
return end;
}
};
二分查找是一个非常简单的思想但细节却非常的重要
这里通过左边界不断逼近右边界最后找到的元素就是第一个大于等于所找元素的值
二分查找和DP相结合还有以下几道题,都是hard难度
俄罗斯套娃
扔鸡蛋