* [做项目（多个C++、Java、Go、测开、前端项目）](https://www.programmercarl.com/other/kstar.html)
* [刷算法（两个月高强度学算法）](https://www.programmercarl.com/xunlian/xunlianying.html)
* [背八股（40天挑战高频面试题）](https://www.programmercarl.com/xunlian/bagu.html)

# 300.最长递增子序列

[力扣题目链接](https://leetcode.cn/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
* -10^4 <= nums[i] <= 104

## 算法公开课

**[《代码随想录》算法视频公开课](https://programmercarl.com/other/gongkaike.html)：：[动态规划之子序列问题，元素不连续！| LeetCode：300.最长递增子序列](https://www.bilibili.com/video/BV1ng411J7xP)，相信结合视频再看本篇题解，更有助于大家对本题的理解**。


## 思路

首先通过本题大家要明确什么是子序列，“子序列是由数组派生而来的序列，删除（或不删除）数组中的元素而不改变其余元素的顺序”。 

本题也是代码随想录中子序列问题的第一题，如果没接触过这种题目的话，本题还是很难的，甚至想暴力去搜索也不知道怎么搜。 
子序列问题是动态规划解决的经典问题，当前下标i的递增子序列长度，其实和i之前的下标j的子序列长度有关系，那又是什么样的关系呢。

接下来，我们依然用动规五部曲来详细分析一波：

1. dp[i]的定义

本题中，正确定义dp数组的含义十分重要。 

**dp[i]表示i之前包括i的以nums[i]结尾的最长递增子序列的长度** 

为什么一定表示  “以nums[i]结尾的最长递增子序” ，因为我们在 做 递增比较的时候，如果比较 nums[j] 和 nums[i] 的大小，那么两个递增子序列一定分别以nums[j]为结尾 和 nums[i]为结尾， 要不然这个比较就没有意义了，不是尾部元素的比较那么 如何算递增呢。 


2. 状态转移方程

位置i的最长升序子序列等于j从0到i-1各个位置的最长升序子序列 + 1 的最大值。

所以：if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1);

**注意这里不是要dp[i] 与 dp[j] + 1进行比较，而是我们要取dp[j] + 1的最大值**。

3. dp[i]的初始化

每一个i，对应的dp[i]（即最长递增子序列）起始大小至少都是1.

4. 确定遍历顺序

dp[i] 是有0到i-1各个位置的最长递增子序列 推导而来，那么遍历i一定是从前向后遍历。

j其实就是遍历0到i-1，那么是从前到后，还是从后到前遍历都无所谓，只要吧 0 到 i-1 的元素都遍历了就行了。 所以默认习惯 从前向后遍历。 

遍历i的循环在外层，遍历j则在内层，代码如下：

```CPP
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] > result) result = dp[i]; // 取长的子序列
}
```

5. 举例推导dp数组

输入：[0,1,0,3,2]，dp数组的变化如下：

![300.最长上升子序列](https://file1.kamacoder.com/i/algo/20210110170945618.jpg)


如果代码写出来，但一直AC不了，那么就把dp数组打印出来，看看对不对！

以上五部分析完毕，C++代码如下：

```CPP
class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        if (nums.size() <= 1) return nums.size();
        vector<int> dp(nums.size(), 1);
        int result = 0;
        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] > result) result = dp[i]; // 取长的子序列
        }
        return result;
    }
};
```
* 时间复杂度: O(n^2)
* 空间复杂度: O(n)



## 总结

本题最关键的是要想到dp[i]由哪些状态可以推出来，并取最大值，那么很自然就能想到递推公式：dp[i] = max(dp[i], dp[j] + 1);

子序列问题是动态规划的一个重要系列，本题算是入门题目，好戏刚刚开始！

## 其他语言版本

### Java：

```Java
class Solution {
    public int lengthOfLIS(int[] nums) {
        if (nums.length <= 1) return nums.length;
        int[] dp = new int[nums.length];
        int res = 1;
        Arrays.fill(dp, 1);
        for (int i = 1; i < dp.length; i++) {
            for (int j = 0; j < i; j++) {
                if (nums[i] > nums[j]) {
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
            res = Math.max(res, dp[i]);
        }
        return res;
    }
}
```

### Python：

DP
```python
class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        if len(nums) <= 1:
            return len(nums)
        dp = [1] * len(nums)
        result = 1
        for i in range(1, len(nums)):
            for j in range(0, i):
                if nums[i] > nums[j]:
                    dp[i] = max(dp[i], dp[j] + 1)
            result = max(result, dp[i]) #取长的子序列
        return result
```
贪心
```python
class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        if len(nums) <= 1:
            return len(nums)
        
        tails = [nums[0]]  # 存储递增子序列的尾部元素
        for num in nums[1:]:
            if num > tails[-1]:
                tails.append(num)  # 如果当前元素大于递增子序列的最后一个元素，直接加入到子序列末尾
            else:
                # 使用二分查找找到当前元素在递增子序列中的位置，并替换对应位置的元素
                left, right = 0, len(tails) - 1
                while left < right:
                    mid = (left + right) // 2
                    if tails[mid] < num:
                        left = mid + 1
                    else:
                        right = mid
                tails[left] = num
        
        return len(tails)  # 返回递增子序列的长度

```
### Go：

```go
// 动态规划求解
func lengthOfLIS(nums []int) int {
    // dp数组的定义 dp[i]表示取第i个元素的时候，表示子序列的长度，其中包括 nums[i] 这个元素
    dp := make([]int, len(nums))

    // 初始化，所有的元素都应该初始化为1
    for i := range dp {
        dp[i] = 1
    }

    ans := dp[0]
    for i := 1; i < len(nums); i++ {
        for j := 0; j < i; j++ {
            if nums[i] > nums[j] {
                dp[i] = max(dp[i], dp[j] + 1)
            }
        }
        if dp[i] > ans {
            ans = dp[i]
        }
    }
    return ans
}

func max(x, y int) int {
    if x > y {
        return x
    }
    return y
}
```
贪心+二分 优化
```go
func lengthOfLIS(nums []int ) int {
  dp := []int{}
  for _, num := range nums {
      if len(dp) == 0 || dp[len(dp) - 1] < num {
      dp = append(dp, num)
    } else {
          l, r := 0, len(dp) - 1
          pos := r
          for l <= r {
              mid := (l + r) >> 1
              if dp[mid] >= num {
                  pos = mid;
                  r = mid - 1
              } else {
                  l = mid + 1
              }
          }
          dp[pos] = num
      }//二分查找
  }
    return len(dp)
}
```

### JavaScript:

```javascript
const lengthOfLIS = (nums) => {
    let dp = Array(nums.length).fill(1);
    let result = 1;

    for(let i = 1; i < nums.length; i++) {
        for(let j = 0; j < i; j++) {
            if(nums[i] > nums[j]) {
                dp[i] = Math.max(dp[i], dp[j]+1);
            }
        }
        result = Math.max(result, dp[i]);
    }

    return result;
};
```

### TypeScript:

```typescript
function lengthOfLIS(nums: number[]): number {
    /**
        dp[i]: 前i个元素中，以nums[i]结尾，最长子序列的长度
     */
    const dp: number[] = new Array(nums.length).fill(1);
    let resMax: number = 0;
    for (let i = 0, length = nums.length; i < length; i++) {
        for (let j = 0; j < i; j++) {
            if (nums[i] > nums[j]) {
                dp[i] = Math.max(dp[i], dp[j] + 1);
            }
        }
        resMax = Math.max(resMax, dp[i]);
    }
    return resMax;
};
```

### C:

```c
#define max(a, b) ((a) > (b) ? (a) : (b))

int lengthOfLIS(int* nums, int numsSize) {
    if(numsSize <= 1){
        return numsSize;
    }
    int dp[numsSize];
    for(int i = 0; i < numsSize; i++){
        dp[i]=1;
    }
    int result = 1;
    for (int i = 1; i < numsSize; ++i) {
        for (int j = 0; j < i; ++j) {
            if(nums[i] > nums[j]){
                dp[i] = max(dp[i], dp[j] + 1);
            }
            if(dp[i] > result){
                result = dp[i];
            }
        }
    }
    return result;
}
```



### Rust:

```rust
pub fn length_of_lis(nums: Vec<i32>) -> i32 {
    let mut dp = vec![1; nums.len()];
    let mut result = 1;
    for i in 1..nums.len() {
        for j in 0..i {
            if nums[j] < nums[i] {
                dp[i] = dp[i].max(dp[j] + 1);
            }
            result = result.max(dp[i]);
        }
    }
    result
}
```

### Cangjie:

```cangjie
func lengthOfLIS(nums: Array<Int64>): Int64 {
    let n = nums.size
    if (n <= 1) {
        return n
    }

    let dp = Array(n, item: 1)
    var res = 0
    for (i in 1..n) {
        for (j in 0..i) {
            if (nums[i] > nums[j]) {
                dp[i] = max(dp[i], dp[j] + 1)
            }
        }
        res = max(dp[i], res)
    }
    return res
}
```


