动态规划,二分查找。

由题,从数组中找一个最长子序列,不难想到,当这个子序列递增子序列的数越接近时是越容易拉长的。从dp上看,当遍历到这个数,会从前面的dp选一个最大的数加上当前数,注意这里的dp是每遍历到一个数都会加进去。而这里的dp数组同样是用来维护到某个数时的ans,nums数组是做了比较的,因此也有可能内循环时数组中的一些数是没有做更新的,因此最后一步肯定是加上当前的数后再进行一次与更新的dp比较进行选最大。
时间复杂度:O(n^2),空间复杂度:O(n)。
java
class Solution {
public int lengthOfLIS(int[] nums) {
int n = nums.length, ans = 0;
int[] f = new int[n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
if (nums[j] < nums[i]) {
f[i] = Math.max(f[i], f[j]);
}
}
f[i]++;
ans = Math.max(ans, f[i]);
}
return ans;
}
}
接着是更快的,用二分查找的方法,在用二分时用mid去找目标值。而这里每遍历到数组的一个数时,同样可以与tails的数去做比较,注意如果遍历到的数与dp的数做比较时mid在大的一边没有移动过,说明这个数就是大的可以追加到原数组的尾巴,即有位置可以插入。
时间复杂度:O(nlogn),空间复杂度:O(n)。
java
class Solution {
public int lengthOfLIS(int[] nums) {
int[] tails = new int[nums.length];
int res = 0;
for(int num : nums) {
int i = 0, j = res-1;
//标准二分,当左右指针重叠时再进行一次比较
while(i <= j) {
int m = (i + j) / 2;
if(tails[m] < num) i = m + 1;
else j = m - 1;
}//这里的i就是目标值
tails[i] = num;//更新这个位置的值
if(res == i) res++;//说明可以进行扩充
//注意每次找到时res肯定会比i多一,因为res从一开始的
}
return res;
}
}
很典型的一道例题,可以用dp的状态维护,找到前面的状态,不过每到一个数都要dp两次。而二分查找目标值的方法,刚好让比目标值小的存到tails数组,比tails数组大的直接追加,以此来更新最长递增子序列。