关闭

Longest Increasing Subsequence

110人阅读 评论(0) 收藏 举报
分类:

Given an unsorted array of integers, find the length of longest increasing subsequence.
For example,
Given [10, 9, 2, 5, 3, 7, 101, 18],
The longest increasing subsequence is [2, 3, 7, 101], therefore the length is 4. Note that there may be more than one LIS combination, it is only necessary for you to return the length.

Your algorithm should run in O(n2) complexity.
Follow up: Could you improve it to O(n log n) time complexity?

思路: 题目的意思是求数组中递增序列的最大长度,序列是可以不连续的。用f(i)表示第0~i个数中递增序列的最大长度。 对于每一个nums[i]要求f(i) 的话,需要找出0~(i-1) 中所有小于nums[i] 的数nums[j],其中 0 <= j <=(i-1),记录下最大的f(j) ,记为max ,那么f(i)=max+1 ,否则找不到比nums[i]还要小的数,f(i)=1,并在整个遍历数组的过程中记录递增序列的最大长度res。
对于数组nums = [10, 9, 2, 5, 3, 7, 101, 18] ,res表示所要求的最大递增序列的长度。

  1. 初始值 f(0) =1 ,更新res=1
  2. 对于 i =1 , nums[1]=9 , 在1之前没有小于nums [i] 的数 ,所以f(1)=1, res=1
  3. 对于i=2 ,nums[2]=2, 在2之前没有小于nums[i]的数,所以f(2)=1,res=1
  4. 对于i=3 ,nums[3]=5 ,在3之前小于5的数有2 ,max=f(2)=1,所以f(5)=max+1=2, 此时更新res=2
  5. 对于i=4,nums[4]=3 ,在4之前小于3的数有 2,f(3)=f(2)+1=2, 此时res=2
  6. 对于i=5,nums[5]=7,在 5之前小于7的数有2,3,5 ,而f(2)=1, f(3)=2,f(5)=2 , max是f(2)、f(3)和f(5)中最大的一个,max=2,则f(7)=3,此时更新res=3
  7. 对于i=6,nums[i]=101, 在6之前小于101的有f(0),f(1),f(2),f(3),f(4),f(5),它们之中最大的为3,则max=3,所以f(6)=max+1=4,此时更新res=4
  8. 同理,对于i=7 ,有f(7)=4 ,此时res=4

最后最长递增子序列的长度为res=4

算法如下,但是时间复杂度为o(n^2)。

public class Solution {
    public int lengthOfLIS(int[] nums) {
        int res=0;
        int n=nums.length;
        if(n==0) return 0;
        int[] f=new int[n];
        f[0]=1; res=1;
        for(int i=1;i<n;i++)
        {
            int max=Integer.MIN_VALUE;
            for(int j=i-1;j>=0;j--)
              {
                  if(nums[j]<nums[i])
                    max= max > f[j] ? max : f[j];
              }

                f[i]=( max!=Integer.MIN_VALUE ? max+1 : 1);
                res= res > f[i] ? res : f[i] ;
        }

        return res;
    }
}

改进: 想到是查找算法,上述算法所用的是线性查找,比线性查找更加快的是二分查找算法,但是二分查找算法需要每次可以折半,如果可以查找一个有序的序列那就好了。 我们可以用一个数组len来记录对应长度的序列末尾最小的一个数字,这话说起来比较绕口,举个例子
比如len[3] 表示 长度为3的序列中末尾数字中最小的一个数

有如下序列:

3,1,2,6,4,-1,5,8,9
比如在数字5之前,长度为3的递增序列有 1,2,6 和1,2,4 这两个序列的末尾数字分别为6和4,其中4比6小,所以记录长度为3的递增序列的末尾数字为len[3]=4

容易证明当   i <j 的时候有 len[i] < len[j] ,  假设j=i+k , i<j =i+k 
len[i]=m ,len[i+k] =n 如果m>n 那就说明长度为i的序列的最后一个数字大于长度为i+k的最后一个数字,这个不合理的,m在n的后面,到n的时候序列的长度已经可以是i+k了,到m的时候长度肯定不止i+k了,所以len[i]=m是不合理的,所以一定有 i<j ,就有len[i] < len[j]

这样,可以在遍历到 数组之下标为s的时候 ,在len[1]至 len[s]中找到一个比nums[s]还要小的数r,r对应的长度为索引 idx,更新f(s)=idx+1 ,如果len[idx+1]大于nums[s],更新len[idx+1]=nums[s]

public class Solution {
    public int lengthOfLIS(int[] nums) {
        int res=0;
         int n=nums.length;
         if(n==0) return 0;
         int[] f=new int[n];
         f[0]=1;
         int[] len=new int[n+1];
         len[1]=nums[0];
         for(int i=1;i<n;i++)
         {
             //二分查找len中小于nums[i]的数
             int j=i;
             while(j>=1 && len[j]==0)
                 j--;
             int right=j;
             int left=1;
             while(left<=right)
             {
                 int mid=left+(right-left)/2;
                 if(len[mid]>=nums[i])
                 {
                     right=mid-1;
                 }else 
                     left=mid+1;

             }
              if(right<left) 
                  f[i]= right+1;
             if(len[f[i]]!=0)
               len[f[i]]= len[f[i]] < nums[i] ? len[f[i]] : nums[i];
             else len[f[i]]=nums[i]; 
             res=res > f[i] ? res : f[i];

         }
         return res!=0 ? res : 1;
    }
}
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:24059次
    • 积分:1664
    • 等级:
    • 排名:千里之外
    • 原创:149篇
    • 转载:0篇
    • 译文:0篇
    • 评论:6条
    文章分类
    最新评论