300. 最长上升/递增子序列(Longest Increasing Subsequence)y优化不是很明朗 复习二分查找

给定一个无序的整数数组,找到其中最长上升子序列的长度。

示例:

输入: [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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值