问题描述
给定一个无序的整数数组,找到其中最长上升子序列的长度。
输
入
输入
输入:
[
10
,
9
,
2
,
5
,
3
,
7
,
101
,
18
]
[10,9,2,5,3,7,101,18]
[10,9,2,5,3,7,101,18]
输
出
输出
输出:
4
4
4
解
释
解释
解释:
最长的上升子序列为:
[
2
,
3
,
7
,
101
]
[2,3,7,101]
[2,3,7,101]
解题报告
动态规划
d
p
[
i
]
dp[i]
dp[i] 表示以第
i
i
i 个数结尾的最长上升子序列的长度。
时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
空间复杂度:
O
(
n
)
O(n)
O(n)
贪心+二分
考虑一个简单的贪心,如果我们要使上升子序列尽可能的长,则我们需要让序列上升得尽可能慢,因此我们希望每次在上升子序列最后加上的那个数尽可能的小。
d
p
[
i
]
dp[i]
dp[i] 表示长度为 i
的最长上升子序列的末尾元素的最小值
可以证明 d p [ ] dp[] dp[] 是一个递增序列
设当前已求出的最长上升子序列的长度为
l
e
n
len
len(初始时为1),从前往后遍历数组
n
u
m
s
nums
nums,在遍历到
n
u
m
s
[
i
]
nums[i]
nums[i] 时:
(1) 如果
n
u
m
s
[
i
]
>
d
p
[
l
e
n
]
nums[i]>dp[len]
nums[i]>dp[len], 则将
d
p
[
l
e
n
+
1
]
dp[len+1]
dp[len+1] 更新成
n
u
m
s
[
i
]
nums[i]
nums[i],同时
l
e
n
len
len 加1;
(2) 否则,在数组
d
p
dp
dp 中二分查找,找到 最后一个 比
n
u
m
s
[
i
]
nums[i]
nums[i] 小的数
d
p
[
k
]
dp[k]
dp[k],然后将
d
p
[
k
+
1
]
dp[k+1]
dp[k+1] 更新成
n
u
m
s
[
i
]
nums[i]
nums[i]。
实现代码
动态规划实现
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n=nums.size();
int ans=-2147483648;
if(n==0)
return 0;
vector<int> dp(n,1);
for(int i=0;i<n;i++){
for(int j=0;j<i;j++){
if(nums[i]>nums[j])
dp[i]=max(dp[i],dp[j]+1);
}
ans=max(ans,dp[i]);
}
return ans;
}
};
贪心+二分实现
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int len = 1, n = (int)nums.size();
if (n == 0) return 0;
vector<int> d(n + 1, 0);
d[len] = nums[0];
for (int i = 1; i < n; ++i) {
if (nums[i] > d[len]) d[++len] = nums[i];
else{
int l = 1, r = len, pos = 0;
// 如果找不到说明所有的数都比 nums[i] 大,此时要更新 d[1],
// 所以这里将 pos 设为 0
while (l <= r) {
int mid = (l + r) >> 1;
if (d[mid] < nums[i]) {
pos = mid;
l = mid + 1;
}
else r = mid - 1;
}
d[pos + 1] = nums[i];
}
}
return len;
}
};
参考资料
[1] Leetcode 300. 最长上升子序列
[2] 官方题解区