# leetcode : [300. 最长递增子序列](https://leetcode-cn.com/problems/longest-increasing-subsequence/)
给你一个整数数组 `nums` ,找到其中最长严格递增子序列的长度。
**子序列** 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,`[3,6,2,7]` 是数组 `[0,3,1,6,2,2,7]` 的子序列。
**示例 1:**
```
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
```
**示例 2:**
```
输入:nums = [0,1,0,3,2,3]
输出:4
```
**示例 3:**
```
输入:nums = [7,7,7,7,7,7,7]
输出:1
```
**提示:**
- `1 <= nums.length <= 2500`
- `-104 <= nums[i] <= 104`
**进阶:**
- 你能将算法的时间复杂度降低到 `O(n log(n))` 吗?
Related Topics
数组
二分查找
动态规划
## 思路1 : 动态规划
dp[i]表示i这个位置的最长子序列长度。默认自身长度为。
对于第i个位置的元素的递增子序列长度,都需要跟这个元素之前的元素进行比较
* 如果第i个位置的值大于之前的元素,那么就需要比较出dp[i]和dp[前面某个位置]+1的最大值。
```java
class Solution {
public int lengthOfLIS(int[] nums) {
int length = nums.length;
int[] dp = new int[length];
int max = 0;
for(int i = 0; i < length;i++){
dp[i] = 1;
//对于第i个位置的元素的递增子序列长度,都需要跟这个元素之前的元素进行比较
for(int j = 0 ; j < i;j++){
//如果第i个位置的值大于之前的元素,那么就需要比较出dp[i]和dp[前面某个位置]+1的最大值。
if(nums[i] > nums[j]){
dp[i] = Math.max(dp[i],dp[j]+1);
}
}
if(dp[i] > max){
max = dp[i];
}
}
return max;
}
}
解答成功:
执行耗时:56 ms,击败了63.84% 的Java用户
内存消耗:40.9 MB,击败了14.55% 的Java用户
时间复杂度O(n^2)
```
## 思路2:贪心+二分查找
参考leetcode官方解答:
* 我们要使上升子序列尽可能长,就要让序列的上升尽可能慢,那就得使每次上升的数字尽可能小。
* 使用dp[i]表示长度为i的上升子序列末尾元素的最小值。使用max表示上升子序列的最大长度。
* 在dp中找到第一个小于当前遍历的元素dp[i-1]<num<dp[i],那么长度为i-1的子序列末尾最小值是dp[i-1],后面添加上num后,长度为i的子序列末尾最小值是num,小于dp[i],所以需要更新dp[i]。
* 因为dp[i]存储的是长度为i的末尾最小值,所以是单调递增的,可以使用二分查找。
算法思路:设max表示最长的子序列长度
* 如果nums[i] > dp[max],dp[++max] = nums[i]。
* 如果nums[i] <= dp[max],对dp进行二分查找,找到dp中第一个小于nums[i]的位置。
```java
class Solution {
public int lengthOfLIS(int[] nums) {
int length = nums.length;
int[] dp = new int[length+1];
//初始化 最长子序列长度为1 长度为1的最小值为nums[0]
int max = 1;
dp[max] = nums[0];
for(int i = 1; i < length;i++){
if(nums[i] > dp[max]){
dp[++max] = nums[i];
}else{
int left = 1 ;
int right = max;
int pos = 0; //记录dp中比nums[i]小的位置 如果找不到比nums的小的位置 默认为0
while(left <= right){
int mid = (left+right)>>1;
if(dp[mid]<nums[i]){//此时第一个小于nums[i]的值一定在[mid,...)范围内
pos = mid;
left = mid + 1;
}else{ //在(...,mid-1)范围内
right = mid-1;
}
}
dp[++pos] = nums[i];
}
}
return max;
}
}
解答成功:
执行耗时:2 ms,击败了99.73% 的Java用户
内存消耗:41.1 MB,击败了5.09% 的Java用户
时间复杂度O(nlogn)
```