300. 最长递增子序列
1. 题目
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
2. 示例
示例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
3. 题解
2种解题思路:动态规划、二分搜索
3.1 动态规划
动态规划的核心思想是数学归纳法
设计动态规划,首先需要一个dp数组,假设dp[0....i - 1]都已经算出来了,然后问自己,怎么通过这些结果算出dp[i]?
dp[i]表示以nums[i]这个数结尾的最长递增子序列的长度。那么以nums[i]结尾的最长递增子序列起码要包含它自己,即要找到前面序列结尾比nums[i]小的子序列,然后把nums[i]拼接上去,就形成了以nums[i]结尾的新的子序列,并在前面子序列的基础上加1。
for j in range(0, i):
if nums[i] > nums[j]:
dp[i] = max(dp[i], dp[j] + 1)
最长子序列,只需要对dp数组遍历一次,取最大值即可。
for i in range(n):
res = max(res, dp[i])
3.2 二分搜索
二分搜索相对来说,难度大一些。以数组[10,9,2,5,3,7,101,18]为例,对其分堆。
首先10,对于第一个元素,单独为堆,此时堆数res = 1;
元素9,从0堆开始找,9小于10,放入0堆,res=1;
元素2,从0堆开始找,2小于9,放入0堆,res=1;
元素5,从0堆开始找,5大于2,此时只有堆0,那么需要再次创建一个堆来存放,res+=1,res=2;
元素3,从0堆开始找,3大于2,再找第1堆,3小于5,放入,res=2;
元素7,7大于2,7大于3,创建新堆,第2堆堆顶元素为7,res=3;
元素101,大于0,1,2堆顶元素,创建新堆,res=4;
元素18,大于0,1,2堆顶元素,放入第3堆,res=4;
输出结果:4(堆的个数就是最长子序列的长度,因为在后面堆里面一定能找到小于等于前面元素)。
4. Code实现
4.1 动态规划
1 class Solution:
2 # 动态规划
3 def lengthOfLIS(self, nums: List[int]) -> int:
4 if len(nums) <= 1:
5 return len(nums)
6 n = len(nums)
7 dp, res = [1 for _ in range(n)], 0
8 for i in range(n):
9 for j in range(0, i):
10 if nums[i] > nums[j]:
11 dp[i] = max(dp[i], dp[j] + 1)
12 res = max(res, dp[i])
13 return res
4.2 二分搜索
1 class Solution:
2 # 二分搜索
3 def lengthOfLISB(self, nums: List[int]) -> int:
4 n = len(nums)
5 if n < 2:
6 return n
7 top = [0 for _ in range(n)]
8 # 堆数
9 res = 0
10 for i in range(n):
11 # 要处理的牌
12 cur = nums[i]
13 # 搜索左边界的二分搜索
14 left, right = 0, res
15 while left < right:
16 mid = (left + right) // 2
17 if top[mid] > cur:
18 right = mid
19 elif top[mid] < cur:
20 left = mid + 1
21 else:
22 right = mid
23 # 没找到合适的堆,新建一堆
24 if left == res:
25 res += 1
26 top[left] = cur
27 return res
5. 结语
努力去爱周围的每一个人,付出,不一定有收获,但是不付出就一定没有收获! 给街头卖艺的人零钱,不和深夜还在摆摊的小贩讨价还价。愿我的博客对你有所帮助(*^▽^*)(*^▽^*)!
如果客官喜欢小生的园子,记得关注小生哟,小生会持续更新(#^.^#)(#^.^#)。
原文地址:https://www.cnblogs.com/haifwu/p/15146460.html
- 通过自定义配置实现插件式设计
- 让IoC动态解析自定义配置(提供基于Unity的实现)
- 如何让ASP.NET默认的资源编程方式支持非.ResX资源存储
- 在VS中通过建立依赖关系使文件结构更清晰
- 一个关于ConfigurationManager.GetSecion方法的小问题
- 追踪记录每笔业务操作数据改变的利器——SQLCDC
- 一个完整的用于追踪数据改变的解决方案
- C# 4.0新特性-"协变"与"逆变"以及背后的编程思想
- 通过内存分析工具来证明字符串驻留机制
- 如果在BackgroundWorker运行过程中关闭窗体…
- 从数据到代码——基于T4的代码生成方式
- 解决T4模板的程序集引用的五种方案
- 编写T4模板进行代码生成无法避免的两个话题:"Assembly Locking"&"Debug"
- 从yield关键字看IEnumerable和Collection的区别
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Vue.js|Nuxt仿制探探堆叠滑动|vue仿Tinder卡片效果
- Elasticsearch:透彻理解 Elasticsearch 中的 Bucket aggregation
- html+js开发模拟考试在线评分系统
- iOS音视频接入 - TRTC接入实时视频通话
- LRU缓存淘汰机制C++实现
- ant-design-vue运行时动态切换主题色
- 使用electron将vue-cli3.x项目打包为桌面应用
- Ubuntu 16.04下安装服务器端Shadowsocks
- 解决vue cli3.x打包上线静态资源找不到路径问题
- Ant-design-vue+vue-i18n实现前端国际化
- Mac OSX终端安装主题(oh my zsh)
- 谷歌浏览器油猴插件安装教程,让你的浏览器更加强大
- zabbix模块配置学习
- Windows下安装及使用NVM
- U盘上安装多个Linux发行版和PE