给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:
可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?
class Solution {
public int lengthOfLIS(int[] nums) {
if(nums.length == 0)
return 0;
int max = 1;//数组长度大于0时,最少为1。
int[] dp = new int[nums.length];
for(int i=0;i<nums.length;i++){
dp[i] = 1;//每一个元素开始都为1
int val = nums[i];
for(int j=0;j<i;j++){
if(val > nums[j]){//当前数的值大于之前的某一个数
dp[i] = Math.max(dp[i],dp[j]+1); //为某个数的dp[i]加上1
max = Math.max(max,dp[i]);
}
}
}
//最大的数不是dp[len-1],而是整个过程最大的数
return max;
}
}
注意:对于一个长度为 N 的序列,最长递增子序列并不一定会以 最后一位数为结尾,因此 dp[N] 不是序列的最长递增子序列的长度,需要遍历 dp 数组找出最大值才是所要的结果。
优化为nlgn
定义一个 tails 数组,其中 tails[i] 存储长度为 i + 1 的最长递增子序列的最后一个元素。对于一个元素 x,
如果它大于 tails 数组所有的值,那么把它添加到 tails 后面,表示最长递增子序列长度加 1;
如果 tails[i-1] < x <= tails[i],那么更新 tails[i-1] = x。
class Solution {
public int lengthOfLIS(int[] nums) {
if(nums.length == 0)
return 0;
int len = 0;//len表示tails数组中已存储的元素的个数,即tails有效数字的长度
int[] tails = new int[nums.length];
for(int key : nums){
int index = binarySearch(tails,len,key);//第一个大于key的位置
tails[index] = key;//覆盖或者增加新值
if(index == len){//index和tails中元素个数相等时加一
len++;
}
}
return len;
}
//找出第一个大于当前数的位置
private int binarySearch(int[] tails,int len,int key){
int low = 0;
int high = len;
while(low < high){
int mid = (high - low) / 2 + low;
int val = tails[mid];
if(val == key){
return mid;
}else if(val > key){//如果当前的值大于key,我们是要查大于key的数,所以这里high赋值为mid,而不是mid-1
high = mid;
}else{
low = mid + 1;
}
}
return low;
}
}