# 最长递增子序列-中等
给你一个整数数组 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
-104 <= nums[i] <= 104
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 方法1: 动态规划
复杂度分析
- 时间复杂度:O(n^2),其中 n 为数组 nums 的长度。动态规划的状态数为 n,计算状态 dp[i] 时,需要 O(n) 的时间遍历 dp[0…i−1] 的所有状态,所以总时间复杂度为 O(n^2)。
- 空间复杂度:O(n)O(n),需要额外使用长度为 nn 的 dpdp 数组。
// https://leetcode-cn.com/problems/longest-increasing-subsequence/solution/zui-chang-shang-sheng-zi-xu-lie-by-leetcode-soluti/
function lengthOfLIS(nums) {
if(!nums || !nums.length) {
return 0
}
let dp = new Array(nums.length)
dp[0] = 1
let maxans = 1
for(let i = 1; i < nums.length; i++) {
dp[i] = 1
for(let j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
dp[i] = Math.max(dp[i], dp[j] + 1)
}
}
maxans = Math.max(maxans, dp[i])
}
return maxans
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 方法2: 贪心+二分查找
复杂度分析
- 时间复杂度:O(nlogn)。数组 nums 的长度为 nn,我们依次用数组中的元素去更新 d 数组,而更新 d 数组时需要进行 O(logn) 的二分搜索,所以总时间复杂度为 O(nlogn)。
- 空间复杂度:O(n),需要额外使用长度为 n 的 d 数组。
// https://leetcode-cn.com/problems/longest-increasing-subsequence/solution/zui-chang-shang-sheng-zi-xu-lie-by-leetcode-soluti/
function lengthOfLIS(nums) {
let len = 1
let n = nums.length
if (n === 0) {
return 0
}
let d = new Array(n + 1)
d[len] = nums[0]
for (let i = 1; i < n; i++) {
if (nums[i] > d[len]) {
d[++len] = nums[i]
} else {
let l = 1, r = len, pos = 0 // 如果找不到说明所有的数都比 nums[i] 大,此时要更新 d[1],所以这里将 pos 设为 0
while(l <= r) {
let mid = Math.floor((l + r) / 2)
if (d[mid] < nums[i]) {
pos = mid
l = mid + 1
} else {
r = mid - 1
}
}
d[pos + 1] = nums[i]
}
}
return len;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 方法3: 另一种二分查找
var lengthOfLIS = function(nums) {
let len = nums.length
if (!len) {
return 0
}
let top = new Array(len)
// 牌堆数初始化为 0
let piles = 0
for (let i = 0; i < len; i++) {
let poker = nums[i]
// 搜索左侧边界的二分查找
let left = 0
let right = piles
while (left < right) {
let mid = Math.floor((left + right) / 2)
if (top[mid] > poker) {
right = mid
} else if (top[mid] < poker) {
left = mid + 1
} else {
right = mid
}
}
if (left === piles) {
// 没有找到合适的牌堆,新建一个堆
// 这里注意 left 是从 0 开始,而堆其实是类似 length,即从1开始,所以当 left 加一 === piles,说明一直往右边找没找到合适的位置,需要新增堆
piles++
}
top[left] = poker // 只需要记录每一堆牌最后一张就行
}
return piles
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34