在数列
{
x
1
,
x
2
,
x
3
,
…
,
x
n
}
\lbrace x_1,x_2,x_3,\ldots,x_n \rbrace
{x1,x2,x3,…,xn}中,寻找最长单调递增子序列
{
x
i
,
x
j
,
…
,
x
k
}
\lbrace x_i,x_j,\ldots,x_k \rbrace
{xi,xj,…,xk}的长度,其中
1
≤
i
<
j
<
k
≤
n
1 \leq i < j<k\leq n
1≤i<j<k≤n同时:
x
i
<
x
j
<
…
<
x
k
x_i<xj<\ldots<x_k
xi<xj<…<xk
例如在
{
1
,
2
,
3
,
4
,
1
,
2
,
3
}
\lbrace1,2,3,4,1,2,3\rbrace
{1,2,3,4,1,2,3}中,最长单调递增子序列为
{
1
,
2
,
3
,
4
}
\lbrace1,2,3,4\rbrace
{1,2,3,4},长度为4。
leetcode题目链接:
最长递增子数列
最长非递减子数列
方法1:
递归暴力搜索出所有可能的结果,然后找出最长的子序列。
c++代码实现:
#include <iostream>
#include <vector>
using namespace std;
int max_len = 0;
// 遍历所有可能的情况
void dfs(vector<int>&nums, int index, int len){
if(len > max_len)
max_len = len;
for(int i = index + 1; i < nums.size(); i++){
if(nums[i] > nums[index]){
dfs(nums, i, len + 1);
}
}
}
int main()
{
// 这里改需要的数值
vector<int> nums = {7,7,7,7,7,7,7};
// 由于要求的是子序列,所以要依次遍历
for(int i = 0; i < nums.size(); i++){
dfs(nums, i, 1);
}
cout << max_len;
return 0;
}
输出:1
时间复杂度:O(n ^ 2)
空间复杂度:O(n) , 这里是递归栈所耗费的空间。
方法2:
动态规划,通过找出前
i
i
i个元素的构成的最长的递增数列长度,进而推出第
i
+
1
i+1
i+1个元素的构成的最长的递增数列长度。
定义
d
p
[
i
]
dp[i]
dp[i]为前
i
i
i个元素中,以第
i
i
i个元素结尾最长的递增数列的长度。
初始值:
d
p
[
0
]
,
d
p
[
1
]
,
…
,
d
p
[
n
−
1
]
=
0
dp[0],dp[1], \ldots,dp[n-1] = 0
dp[0],dp[1],…,dp[n−1]=0
状态转移方程:
d
p
[
j
]
=
m
a
x
(
d
p
[
i
]
)
+
1
当
0
≤
i
<
j
并
且
n
u
m
s
[
i
]
<
n
u
m
s
[
j
]
dp[j]=max(dp[i]) + 1 当0\leq i < j 并且nums[i]<nums[j]
dp[j]=max(dp[i])+1当0≤i<j并且nums[i]<nums[j]
代码:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
// 这里改需要的数组
vector<int> nums = {10,9,2,5,3,7,101,18};
// 这里设置dp数组
vector<int> dp(nums.size(), 0);
// 逐个扫描一次
for(int i = 0; i < nums.size(); i++){
dp[i] = 1;
for(int j = i - 1; j >= 0; j--){
if(nums[i] > nums[j])
dp[i] = max(dp[j] + 1, dp[i]);
}
}
cout<<*max_element(dp.begin(), dp.end());
return 0;
}
输出:4
时间复杂度:O(
n
2
n^2
n2)
空间复杂度:O(n)
方法3:
贪心加二分。
我们想让递增数列尽可能地长,就要让数列里的值尽可能地校,保证有更多的元素能够加入到数列里。
设立dp数组,其中
d
p
[
i
]
dp[i]
dp[i]表示由
i
i
i个元素构成的递增数列末尾元素的最小值。
设当前最长上升子列的长度值为1,从前向后遍历nums数组.
如果,nums[i]>dp[len],则dp[++len]=nums[i]。
否则,就用二分法查找元素里第一个大于或等于nums[i]的数,nums[i]替换该值。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
// 这里改需要的数组
vector<int> nums = {10,9,2,5,3,7,101,18};
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];
}
}
cout<<len;
return 0;
}
输出:4
时间复杂度:O(
n
l
o
g
n
nlogn
nlogn)
空间复杂度:O(
n
n
n)