leetcode-master/problems/0300.最长上升子序列.md

272 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<p align="center">
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
<img src="https://code-thinking-1253855093.file.myqcloud.com/pics/20210924105952.png" width="1000"/>
</a>
<p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 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
## 思路
最长上升子序列是动规的经典题目这里dp[i]是可以根据dp[j] j < i推导出来的那么依然用动规五部曲来分析详细一波
1. dp[i]的定义
**dp[i]表示i之前包括i的以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遍历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://img-blog.csdnimg.cn/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;
}
};
```
## 总结
本题最关键的是要想到dp[i]由哪些状态可以推出来并取最大值那么很自然就能想到递推公式dp[i] = max(dp[i], dp[j] + 1);
子序列问题是动态规划的一个重要系列,本题算是入门题目,好戏刚刚开始!
## 其他语言版本
Java
```Java
class Solution {
public int lengthOfLIS(int[] nums) {
int[] dp = new int[nums.length];
Arrays.fill(dp, 1);
for (int i = 0; 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);
}
}
}
int res = 0;
for (int i = 0; i < dp.length; i++) {
res = Math.max(res, dp[i]);
}
return res;
}
}
```
Python
```python
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
if len(nums) <= 1:
return len(nums)
dp = [1] * len(nums)
result = 0
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
```
Go
```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)
}
```
```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
}
```
Rust:
```rust
pub fn length_of_lis(nums: Vec<i32>) -> i32 {
let mut dp = vec![1; nums.len() + 1];
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
}
```
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;
};
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>