Description
Given an integer array nums, return the length of the longest strictly increasing subsequence.
A subsequence is a sequence that can be derived from an array by deleting some or no elements without changing the order of the remaining elements. For example, [3,6,2,7] is a subsequence of the array [0,3,1,6,2,2,7].
Examples
Example 1:
Input: nums = [10,9,2,5,3,7,101,18]
Output: 4
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.
Example 2:
Input: nums = [0,1,0,3,2,3]
Output: 4
Example 3:
Input: nums = [7,7,7,7,7,7,7]
Output: 1
Constraints:
1 <= nums.length <= 2500
− 1 0 4 -10^4 −104 <= nums[i] <= 1 0 4 10^4 104
Follow up: Can you come up with an algorithm that runs in O(n log(n)) time complexity?
思路
看到题目的时候就感觉要用DP,用一个数组来存储截止到当前节点为止,以当前节点为结尾的最长subsequence长度,但这种dp的时间复杂度是 O ( n 2 ) O(n^2) O(n2),效率不是很高
这种情况下,就去翻了solution,有一个用tail+二分查找的方法,这里简单介绍一下(别人的)方法。
首先建立一个tails数组,他的下标
i
i
i表示长度为
i
+
1
i+1
i+1的subsequence,他的最小值结尾是多少,以
[
4
,
10
,
4
,
3
,
8
,
9
]
[4, 10, 4, 3, 8, 9]
[4,10,4,3,8,9] 为例
- tails[0] = 3 (长度为1的subsequence [4], [10], [3], [8], [9] 中,最小结尾值是3)
- tails[1] = 8 (长度为2的subsequence [4, 10], [4, 8], [4, 9], [3, 8], [3, 9] 中,最小结尾值是8)
- tails[2] = 9 (长度为3的subsequence [4, 8, 9], [3, 8, 9] 中,最小结尾值是9)
所以最长的subsequence长度就是tail.size() = 3
根据上面的例子不难发现,tail是一个递增序列,同时,更新规则如下,遍历数组nums
- 如果当前数值nums[i] > tails[tails.length - 1],说明至少可以通过长度为tails.length的subsequence+当前数值组成一个长度为tails.length+1的subsequence,因此添加tails[tails.length] = nums[i]
- 如果当前数值nums[i] > tails[tails.length - 1],那么在tails中找到比他大的数值当中最小的那个tail[k],并将tail[k]更新为nums[i],这个可以理解为 tail[k-1] + nums[i] 组成了新的tail[k]
第二种可能性就可以通过二分查找进行O(logn)优化。
代码
O ( n 2 ) O(n^2) O(n2)
class Solution {
public int lengthOfLIS(int[] nums) {
int[] dp = new int[nums.length];
for(int i = 0; i < nums.length; i++){
dp[i] = 1;
for (int j = 0; j < i; j++){
if(nums[j] < nums[i])
dp[i] = Math.max(dp[j] + 1, dp[i]);
}
}
int max = dp[0];
for(int i = 1; i < nums.length; i++)
max = Math.max(dp[i], max);
return max;
}
}
O ( n log n ) O(n\log n) O(nlogn)
class Solution {
public void addElement(List<Integer> ls, int start, int end, int num){
if (start >= end){
if (ls.get(start) >= num)
ls.set(start, num);
else if (start < ls.size() - 1)
ls.set(start + 1, num);
else
ls.add(num);
return;
}
int mid = (start + end) / 2;
if (ls.get(mid) == num)
return;
else if (ls.get(mid) > num)
addElement(ls, start, mid - 1, num);
else
addElement(ls, mid + 1, end, num);
}
public int lengthOfLIS(int[] nums) {
List<Integer> tails = new ArrayList<>();
tails.add(nums[0]);
for(int i = 1; i < nums.length; i++){
addElement(tails, 0, tails.size() - 1, nums[i]);
}
return tails.size();
}
}