Create 300. Longest Increasing Subsequence.md

This commit is contained in:
ShusenTang 2019-11-08 23:59:22 +08:00 committed by GitHub
parent 7fd60bedf7
commit 53570dab62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -0,0 +1,113 @@
# [300. Longest Increasing Subsequence](https://leetcode.com/problems/longest-increasing-subsequence/)
# 思路
给定一个数组,求最长递增子序列的长度。
## 思路一、O(n^2)
很明显是一个动态规划题我们可以用dp[i]表示以nums[i]为结尾的最长子序列的长度。这样更新dp[i]的转移方程就为
```
for all j in [0, i-1]:
if nums[i] >= nums[j]:
dp[i] = max(dp[i], dp[j] + 1)
```
从转移方程可以看出有两层循环所以时间复杂度为O(n^2)。
## 思路二、O(nlogn)
思路一比较好想O(nlogn)的就不太好想了,可参考[这里](https://segmentfault.com/a/1190000003819886),讲得比较清楚。
核心思路就是:
> 维护一个长度可变的数组tails, tails[i]表示长度为i+1的所有递增子序列中最小的那个末尾元素这样更新完毕后tails的长度即结果。
下面举个[例子](https://segmentfault.com/a/1190000003819886)说明:
在`1,3,5,2,8,4,6`这个例子中当到6时我们一共可以有四种长度的递增子序列在每种子序列中我们取末尾元素最小的那么这四个序列分别是
```
1
1,2
1,3,4
1,3,5,6
```
为什么要选末尾元素最小的呢,是因为在长度一定的情况下,末尾元素最小的是未来最有可能成为最长序列的候选人。这样,每来一个新的数,
我们便按照以下规则更新这些序列:
1. 如果nums[i]比所有序列的末尾都大或等于最大末尾说明有更长的递增序列产生我们把最长的序列复制一遍并加上这个nums[i]。
2. 如果nums[i]比所有序列的末尾都小说明长度为1的序列可以更新了更新为这个更小的末尾。
3. 如果在中间,则更新那个末尾数字刚刚大于等于自己的那个序列,说明那个长度的序列可以更新了。
比如这时如果再来一个9那就是第1种情况更新序列为
```
1
1,2
1,3,4
1,3,5,6
1,3,5,6,9
```
如果再来一个3那就是第3种情况更新序列为
```
1
1,2
1,3,3
1,3,5,6
```
如果再来一个0那就是第2种情况更新序列为
```
0
1,2
1,3,3
1,3,5,6
```
前两种都很好处理O(1)就能解决主要是第三种情况实际上我们观察直到6之前这四个不同长度的升序序列他们末尾是递增的
所以可以用二分搜索来找到适合的更新位置。
# C++
## 思路一
``` C++
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
if(n <= 1) return n;
vector<int>dp(n, 1);
int res = 1;
for(int i = 1; i < n; i++){
// j >= 0 可优化成 j >= dp[i]-1
for(int j = i-1; j >= 0; j--)
if(nums[i] > nums[j])
dp[i] = max(dp[i], dp[j] + 1);
res = max(res, dp[i]);
}
return res;
}
};
```
## 思路二
``` C++
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
if(n <= 1) return n;
vector<int>tails;
tails.push_back(nums[0]);
for(int i = 1; i < n; i++){
if(nums[i] > tails.back()) tails.push_back(nums[i]);
else{
tails[lower_bound(tails.begin(), tails.end(), nums[i]) - tails.begin()] = nums[i];
// 上面这句等价于下面注释部分
// int low = 0, high = tails.size() - 1;
// while(low < high){
// int mid = low + (high - low) / 2;
// if(tails[mid] < nums[i]) low = mid + 1;
// else high = mid;
// }
// tails[low] = nums[i];
}
}
return tails.size();
}
};
```