题目描述
给出一个未排序的整数数组,找出最长递增子序列的长度。
样例
输入: [10,9,2,5,3,7,101,18]
输出:4
说明:最长递增子序列为[2,3,7,101],长度为4,可能有多个可能的最长递增子序列,此题只需要返回长度即可
算法1
(动态规划)O(n^2)
用数组dp[i]记录以nums[i]结尾(即nums[i]为最后一个数字)的最长递增子序列的长度,则递推方程为 dp[i]=max(dp[j]+1),其中要求1≤j<i且nums[j]<nums[i]nums[j]<nums[i]。
时间复杂度分析:对每个i(1≤i≤ni,都需要从1遍历到i,则时间复杂度为O(n^2),空间复杂度的话需要一个额外的dp数组,空间复杂度为O(n^2)。
代码如下:
int lengthOfLIS(vector<int>& nums) {
if(nums.size()==0)
return 0;
vector<int> dp(nums.size(),1);//这个之所以赋值为1,是因为下面dp[i]=max(dp[i],dp[j]+1)这个在计算的时候如 果 //dp[i]=max(dp[i],dp[j]+1)的时候,dp[i]的第二个实际上是2,但是如果全部初始化为0.则dp[i]=max(0,0+1)=1,但实际上是2,
int res = 1;
//维持递增序列的就是j在i之后,然后用if(nums[i]>nums[j]),还有这个是序列,所以是两个for循环的,一个字符一个字符比较
for(int i= 1;i<nums.size();i++){
for(int j = 0;j<i;j++){
if(nums[i]>nums[j])
dp[i] = max(dp[i],dp[j]+1);
}
if(dp[i]>res)
res = dp[i];
}
return res;
}
也可以看下面的:
int lengthOfLIS(vector<int>& nums) {
if(nums.size()==0)
return 0;
vector<int> dp(nums.size(),0);//这个之所以赋值为1,是因为下面dp[i]=max(dp[i],dp[j]+1)这个在计算的时候如 果 //dp[i]=max(dp[i],dp[j]+1)的时候,dp[i]的第二个实际上是2,但是如果全部初始化为0.则dp[i]=max(0,0+1)=1,但实际上是2,
int res = 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[i],dp[j]+1);
}
if(dp[i]+1>res)
res = dp[i]+1;
}
return res;
}
方法2:动态规划+二分(时间复杂度是O(logN) ,空间复杂度是O(1))
说实话,正常人基本想不到这种解法(也许玩过某些纸牌游戏的人可以想出来)。所以如果大家了解一下就好,正常情况下能够给出动态规划解法就已经很不错了。
为了简单期间,后文跳过所有数学证明,通过一个简化的例子来理解一下思路。
首先,给你一排扑克牌,我们像遍历数组那样从左到右一张一张处理这些扑克牌,最终要把这些牌分成若干堆。
最长递增子序列和一种叫做 patience game 的纸牌游戏有关,甚至有一种排序方法就叫做 patience sorting(耐心排序)。
为了简单期间,后文跳过所有数学证明,通过一个简化的例子来理解一下思路。
首先,给你一排扑克牌,我们像遍历数组那样从左到右一张一张处理这些扑克牌,最终要把这些牌分成若干堆。
处理这些扑克牌要遵循以下规则:
只能把点数小的牌压到点数比它大的牌上。如果当前牌点数较大没有可以放置的堆,则新建一个堆,把这张牌放进去。如果当前牌有多个堆可供选择,则选择最左边的堆放置。
比如说上述的扑克牌最终会被分成这样 5 堆(我们认为 A 的值是最大的,而不是 1)。
为什么遇到多个可选择堆的时候要放到最左边的堆上呢?因为这样可以保证牌堆顶的牌有序(2, 4, 7, 8, Q),证明略。
按照上述规则执行,可以算出最长递增子序列,牌的堆数就是最长递增子序列的长度,证明略。
我们只要把处理扑克牌的过程编程写出来即可。每次处理一张扑克牌不是要找一个合适的牌堆顶来放吗,牌堆顶的牌不是有序吗(即2 4 7 8 Q),这就能用到二分查找了:用二分查找来搜索当前牌应放置的位置。
PS:旧文二分查找算法详解详细介绍了二分查找的细节及变体,这里就完美应用上了。如果没读过强烈建议阅读。
作者:labuladong
链接:https://leetcode-cn.com/problems/longest-increasing-subsequence/solution/dong-tai-gui-hua-she-ji-fang-fa-zhi-pai-you-xi-jia/
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
//int v[nums.size()]={0};
vector<int>v(nums.size(), 0);
if (nums.size() == 0) return 0;
// 牌堆数初始化为 0
int piles = 0;
for (int i = 0; i<nums.size(); i++)
{
// 要处理的扑克牌,即将这个扑克牌插到哪里
int poker = nums[i];
/***** 搜索左侧边界的二分查找 *****/
int left = 0, right = piles;//right是堆数
while (left<right)
{
int mid = (left + right) / 2;
if (v[mid] >= poker)//如果中间位置的数是大于要插入的扑克
{
right = mid;
}
else
{
left = mid + 1;
}
}
//没找到合适的牌堆,新建一堆
if (left == piles)piles++;
v[left] = poker;
}
return piles;
}
};