两种方法:
1.dp 时间复杂度O(n^2)
2.贪心 + 二分 时间复杂度 O(n*log(n))
1.dp1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
if (nums.size() == 0) return 0;
vector<int> b;
b.push_back(1);
int maxx = 1;
for (int i = 1; i < nums.size(); i++) {
int m = 0;
for (int j = 0; j < i; j++) {
if (nums[j] < nums[i] && b[j] > m) {
m = b[j];
}
}
b.push_back(m+1);
maxx = max(b[i], maxx);
}
return maxx;
}
};
2.贪心 + 二分
a[i]表示第i个数据。
dp[i]表示表示长度为i+1的LIS结尾元素的最小值。
利用贪心的思想,对于一个上升子序列,显然当前最后一个元素越小,越有利于添加新的元素,这样LIS长度自然更长。
因此,我们只需要维护dp数组,其表示的就是长度为i+1的LIS结尾元素的最小值,保证每一位都是最小值,
这样子dp数组的长度就是LIS的长度。
dp数组具体维护过程同样举例讲解更为清晰。
同样对于序列 a(1, 7, 3, 5, 9, 4, 8),dp的变化过程如下:
dp[0] = a[0] = 1,长度为1的LIS结尾元素的最小值自然没得挑,就是第一个数。 (dp = {1})
对于a[1]=7,a[1]>dp[0],因此直接添加到dp尾,dp[1]=a[1]。(dp = {1, 7})
对于a[2]=3,dp[0]< a[2]< dp[1],因此a[2]替换dp[1],令dp[1]=a[2],因为长度为2的LIS,结尾元素自然是3好过于7,因为越小这样有利于后续添加新元素。 (dp = {1, 3})
对于a[3]=5,a[3]>dp[1],因此直接添加到dp尾,dp[2]=a[3]。 (dp = {1, 3, 5})
对于a[4]=9,a[4]>dp[2],因此同样直接添加到dp尾,dp[3]=a[9]。 (dp = {1, 3, 5, 9})
对于a[5]=4,dp[1]< a[5]< dp[2],因此a[5]替换值为5的dp[2],因此长度为3的LIS,结尾元素为4会比5好,越小越好嘛。(dp = {1, 3, 4, 9})
对于a[6]=8,dp[2]< a[6]< dp[3],同理a[6]替换值为9的dp[3],道理你懂。 (dp = {1, 3, 5, 8})
这样子dp数组就维护完毕,所求LIS长度就是dp数组长度4。
通过上述求解,可以发现dp数组是单调递增的,因此对于每一个a[i],先判断是否可以直接插入到dp数组尾部,
即比较其与dp数组的最大值即最后一位;如果不可以,则找出dp中第一个大于等于a[i]的位置,用a[i]替换之。
这个过程可以利用二分查找,因此查找时间复杂度为O(logN),所以总的时间复杂度为O(N*logN)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
35
36
37
38class Solution {
public:
int binary(vector<int> v, int x) {
int i = 0, j = v.size() - 1;
while (i < j) {
int mid = i + (j - i) / 2;
if (v[mid] >= x) {
j = mid;
} else {
i = mid + 1;
}
}
return i;
}
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
if (n == 0) {
return 0;
}
int len = 0;
vector<int> dp;
dp.push_back(nums[0]);
for (int i = 1; i < n; i++) {
if (nums[i] > dp[len]) {
dp.push_back(nums[i]);
len++;
} else {
int id = binary(dp, nums[i]);
dp[id] = nums[i];
}
}
return len + 1;
}
};