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

128 lines
4.2 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
<a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a>
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
</p>
<p align="center"><strong>欢迎大家参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
## 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
* -10^4 <= nums[i] <= 104
## 思路
最长上升子序列是动规的经典题目这里dp[i]是可以根据dp[j] j < i推导出来的那么依然用动规五部曲来分析详细一波
1. dp[i]的定义
**dp[i]表示i之前包括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则在内层代码如下
```C++
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++代码如下:
```C++
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;
}
};
```
杨老师的这个专栏很不错他本身也是Oracle 首席工程师对Java有极其深刻的理解讲的内容很硬核适合使用Java语言的录友们用来进阶作为面试突击手册非常合适 所以推荐给大家现在下单输入口令javahexin可以省40元那[机智]
## 总结
本题最关键的是要想到dp[i]由哪些状态可以推出来并取最大值那么很自然就能想到递推公式dp[i] = max(dp[i], dp[j] + 1);
子序列问题是动态规划的一个重要系列,本题算是入门题目,好戏刚刚开始!
## 其他语言版本
Java
Python
Go
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=../pics/公众号.png width=450 alt=> </img></div>