题干
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:
可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?
想法
这又是一道很sb的题,每次leetcode都这样,不把题说清楚。
这个最长上升子序列的意思是:可以不连续
动态规划,比较这个数之前的所有数,如果比它小就加进来。
O(n log n)就是结合二分法来找小的数
状态转换方程是dp[i+1]=max(dp[i],dp[i]+1);
java代码
class Solution {
public int lengthOfLIS(int[] nums) {
int chang=nums.length;
if(chang==0){
return 0;
}
if(chang==1){
return 1;
}
int[] len=new int[chang];
for(int i=0;i<chang;i++){
len[i]=1;
}
for(int j=0;j<chang;j++){
for(int k=0;k<j;k++){
if(nums[j]>nums[k]&&len[j]<len[k]+1){
len[j]=len[j]+1;
}
}
}
int maxLen = 0;
for (int i = 0; i < nums.length ; i++) {
if (maxLen < len[i]) {
maxLen = len[i];
}
}
return maxLen;
}
}
leetcode大佬的代码
class Solution {
public int lengthOfLIS(int[] nums) {
int len=nums.length;
if(len<=1) return len;
// int[] dp=new int [len];
// Arrays.fill(dp,1);
// int res=0;
// for (int i = 0; i < len; i++) {
// for (int j = 0; j < i; j++) {
// if(nums[j]<nums[i]){
// dp[i]= Math.max(dp[i],dp[j]+1);
// }
// }
// res=Math.max(res,dp[i]);
// }
// return res;
int[] tail=new int[ len];
tail[0]=nums[0];
int end=0;
for (int i = 1; i < nums.length; i++) {
if(nums[i]>tail[end]){
tail[++end]=nums[i];
}else{
int l=0;
int r=end;
while(l<r){
int m=(l+r)>>>1;
if(tail[m]<nums[i]){
l=m+1;
}else{
r=m;
}
}
tail[l]=nums[i];
}
}
return end+1;
}
}
class Solution {
public int lengthOfLIS(int[] nums) {
// if (nums == null || nums.length <= 0) {
// return 0;
// }
// int[] dp = new int[nums.length];
// if (nums.length == 1) {
// return 1;
// }
// dp[0] = 1;
// int max = dp[0];
// for (int i = 1; i < nums.length; i++) {
// dp[i] = 1;//一开始缺少这个语句,所以导致出错
// for (int j = 0; j < i; j++) {
// if (nums[i]>nums[j]) {
// dp[i] = Math.max(dp[i], dp[j]+1);
// }
// }
// max = Math.max(dp[i],max);
// }
// return max;
int[] dp = new int[nums.length];
int len = 0;
for (int num : nums) {
int i = Arrays.binarySearch(dp, 0, len, num);
if (i < 0) {
i = -(i + 1);
}
dp[i] = num;
if (i == len) {
len++;
}
}
return len;
}
}
class Solution {
public int lengthOfLIS(int[] nums) {
int result = 1;
int length = nums.length;
if(length == 0||length==1){
return length;
}
int[] dp = new int[length];
//tails[k] 表示长度为k+1的最长上升子序列的最后一个元素的值。如果有多个相同长度的子序列,结尾元素最小的。
int[] tails = new int[length];
for (int i = 0; i < length; i++) {
tails[i] = Integer.MAX_VALUE;
}
tails[0] = nums[0];
dp[0] = 1;
for(int i =1;i<length;i++){
int temp = binarySearch(tails,i-1,nums[i]);
if(temp<0){
dp[i] = 1;
tails[0] = Math.min(nums[i],tails[0]);
}else{
dp[i] = temp+1+1;
tails[dp[i]-1] = Math.min(nums[i],tails[dp[i]-1]);
result= Math.max(result,dp[i]);
}
}
return result;
}
//找到前k个元素比其小的最后一个元素(数值上最靠近的)
public int binarySearch(int[] nums,int r,int value){
int l = 0;
int mid = (0+r)/2;
while(l<=r){
if(nums[mid]>value){
r = mid -1;
}else if (nums[mid] <value){
l = mid + 1;
}else{
return mid-1;
}
mid = (l+r)/2;
}
return r;
}
}