给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:
可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
法1:动态规划
参考:动态规划 + 贪心算法(二分法)(Python 代码、Java 代码)
定义状态:dp[i] 表示以第 i 个数字为结尾的最长上升子序列的长度。即在 [0, ..., i] 的范围内,选择 以数字 nums[i] 结尾 可以获得的最长上升子序列的长度。注意:以第 i 个数字为结尾,即 要求 nums[i] 必须被选取。反正一个子序列一定会以一个数字结尾,那我就将状态这么定义,这一点是常见的。
状态转移方程:遍历到索引是 i 的数的时候,我们应该把索引是 [0, ... ,i - 1] 的 dp 都看一遍,如果当前的数 nums[i] 严格大于之前的某个数,那么 nums[i] 就可以接在这个数后面形成一个更长的上升子序列。把前面的 i 个数都看了,dp[i] 就是它们的最大值加 1。即比当前数要小的那些里头,找最大的,然后加 1 。
总结一下,状态转移方程是:
dp[i] = max{dp[i], dp[j]+1}
最后不要忘记,扫描一遍这个 dp 数组,其中最大值的就是题目要求的最长上升子序列的长度。
复杂度分析:
时间复杂度:O(N^2)因为有两个 for 循环,每个 for 循环的时间复杂度都是线性的。
空间复杂度:O(N),要开和数组等长的状态数组,最后要再看一遍状态数组的最大值。
class Solution:
def lengthOfLIS(self, nums):
length = len(nums)
if length <= 1:
return length
dp = [1] * length
for i in range(1, length):
for j in range(i):
if nums[i] > nums[j]:
dp[i] = max(dp[i], dp[j] + 1)
return max(dp) # 最后要全部看一遍,取最大值
#dp[i]表示以nums[i]为结尾时的最大长度。
#当nums[i]比前面的元素大时,就可以将nums[i]放入前面找到的最长递增子序列的后面构成更长的递增子序列。
#因为前面满足nums[i]>nums[j]的j很多(j=0,1,...,i-1),所以需要更新dp[i],所以才会有max(dp[i],dp[j]+1)