Leetcode300 最长上升子序列
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-increasing-subsequence
博主Github:https://github.com/GDUT-Rp/LeetCode
题目:
给定一个无序的整数数组,找到其中最长上升子序列的长度。c
示例 1:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:
- 可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
- 你算法的时间复杂度应该为 O( n 2 n^2 n2) 。
解题思路:
方法一:带记忆的递归
直观想法
在前面的方法中,许多递归调用必须使用相同的参数进行一次又一次的调用。通过将为特定调用获得的结果存储在二维记忆数组 memo 中,可以消除这种冗余。
m
e
m
o
[
i
]
[
j
]
memo[i][j]
memo[i][j] 表示使用
n
u
m
s
[
i
]
nums[i]
nums[i] 作为上一个被认为包含/不包含在 lis
中的元素的 lis
可能的长度,其中
n
u
m
s
[
j
]
nums[j]
nums[j] 作为当前被认为包含/不包含在 lis
中的元素。这里,
n
u
m
s
nums
nums 表示给定的数组。
C++
class Solution {
public:
int lengthOfLIS(vector<int> &nums) {
int len = nums.size();
}
};
Java
public class Solution {
public int lengthOfLIS(int[] nums) {
int memo[][] = new int[nums.length + 1][nums.length];
for (int[] l : memo) {
Arrays.fill(l, -1);
}
return lengthofLIS(nums, -1, 0, memo);
}
public int lengthofLIS(int[] nums, int previndex, int curpos, int[][] memo) {
if (curpos == nums.length) {
return 0;
}
if (memo[previndex + 1][curpos] >= 0) {
return memo[previndex + 1][curpos];
}
int taken = 0;
if (previndex < 0 || nums[curpos] > nums[previndex]) {
taken = 1 + lengthofLIS(nums, curpos, curpos + 1, memo);
}
int nottaken = lengthofLIS(nums, previndex, curpos + 1, memo);
memo[previndex + 1][curpos] = Math.max(taken, nottaken);
return memo[previndex + 1][curpos];
}
}
Python
class Solution:
def nthUglyNumber(self, n: int) -> int:
复杂度分析
时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)。递归树的大小可以达到
n
2
n^2
n2。
空间复杂度:
O
(
n
2
)
O(n^2)
O(n2),使用
n
∗
n
n*n
n∗n 的
m
e
m
o
memo
memo 数组。
方法二:动态规划
他的方法依赖于这样一个事实,在给定数组中最长上升子序列可能达到
i
t
h
i^{th}
ith
独立于后面在数组中出现的元素。因此,如果我们知道 lis
的长度不超过
i
t
h
i^{th}
ith,我们可以根据索引为
j
j
j 的元素包括
(
i
+
1
)
t
h
(i+1)^{th}
(i+1)th 元素来计算 lis
的长度,其中
0
≤
j
≤
(
i
+
1
)
0\leq j\leq (i + 1)
0≤j≤(i+1)。
我们使用
d
p
dp
dp 数组来存储所需的数据。
d
p
[
i
]
dp[i]
dp[i] 表示考虑到数组元素一直到
i
t
h
i^{th}
ith 的情况下可能的最长上升子序列的长度,必须包括
i
t
h
i^{th}
ith 元素。为了找出
d
p
[
i
]
dp[i]
dp[i],我们需要尝试在每个可能的最长上升子序列中附加当前元素
(
n
u
m
s
[
i
]
)
(nums[i])
(nums[i])到
(
i
−
1
)
t
h
(i-1)^{th}
(i−1)th(包括
(
i
−
1
)
t
h
(i-1)^{th}
(i−1)th),这样通过添加当前元素形成的新序列也是一个上升子序列。因此,我们可以很容易地确定
d
p
[
i
]
dp[i]
dp[i] 使用:
d
p
[
i
]
=
max
(
d
p
[
j
]
)
+
1
,
∀
0
≤
j
<
i
dp[i] = \text{max}(dp[j]) + 1, \forall 0\leq j < i
dp[i]=max(dp[j])+1,∀0≤j<i
最后,确定最终结果的所有
d
p
[
i
]
dp[i]
dp[i] 中的最大值。
L I S l e n g t h = max ( d p [ i ] ) , ∀ 0 ≤ i < n LIS_{length}= \text{max}(dp[i]), \forall 0\leq i < n LISlength=max(dp[i]),∀0≤i<n
C++
class Solution {
public:
int lengthOfLIS(vector<int> &nums) {
int len = nums.size();
if (len == 0) return 0;
vector<int> dp(len, 1);
dp[0] = 1;
int maxans = 1;
for (int i = 1; i < len; ++i) {
int maxval = 0;
for (int j = 0; j < i; ++j) {
if (nums[i] > nums[j]) {
maxval = max(maxval, dp[j]);
}
}
dp[i] = maxval + 1;
maxans = max(maxans, dp[i]);
}
return maxans;
}
};
Java
public class Solution {
public int lengthOfLIS(int[] nums) {
if (nums.length == 0) {
return 0;
}
int[] dp = new int[nums.length];
dp[0] = 1;
int maxans = 1;
for (int i = 1; i < dp.length; i++) {
int maxval = 0;
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
maxval = Math.max(maxval, dp[j]);
}
}
dp[i] = maxval + 1;
maxans = Math.max(maxans, dp[i]);
}
return maxans;
}
}
Python
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
复杂度分析
- 时间复杂度: O ( n 2 ) O(n^2) O(n2)。有两个 n n n的循环。
- 空间复杂度: O ( n ) O(n) O(n),用了大小为 n n n 的矩阵 d p dp dp。