leetcode 300.Longest Increasing Subsequence(最长递增子序列) O(nlogn)算法

leetcode 300.Longest Increasing Subsequence(最长递增子序列) ,网上多是动态规划,复杂度为O(n^2)算法;本文设计一个O(nlogn)算法,即维护最小的最长子序列

题目:


题目大意:求一个无序数组的最长递增子序列。这里的子序列,不要求是连续子序列。比如文中[10,9,2,5,3,7,101,18]最长子序列为[2,3,7,101],长度为4。

最简单的思路,动态规划,纪录之前所有位置的最长长度,复杂度为O(n^2)。

代码如下(c++,O(n^2),可AC):

     int lengthOfLIS(vector<int>& nums) {
        if(nums.size()<=1)  return nums.size();

        vector<int> b(nums.size(),1);

int res=0;
        for(int i=1;i<nums.size();i++){
            for(int j=0;j<i;j++){
                if(nums[i]>nums[j])
                    b[i]=max(b[i],b[j]+1);
            }
            res=max(res,b[i]);
        }
        return res;
    }


显然,有更好的办法。这个办法思路是这样的:对于一个子序列,如[10,9,2,5,3,7,101,18],[2,3,7,101],[2,5,7,101],[2,3,7,18],[2,5,7,18]都是它的最长子序列,但我们没必要纪录这么多子序列,我们只需纪录对我们最有用的子序列,显然其中最小的子序列最有用,比如如果我再在[10,9,2,5,3,7,101,18]插入20,显然[2,3,7,18]最好处理,它后面直接插入19即可,得[2,3,7,18,20]。你可能看一下下面具体例子,可能更好理解。

维护一个最长递增子序列vector<int> v,且里面的数字最小,举例[10,9,2,5,3,7,101,18]:处理第一个数10,则v=[10];处理第二个数9,9<=10,则更新v=[9];处理第三个数2,2<=9,更新v=[2];处理第四个数5,5>2,插入v尾部,v=[2,5];处理第五个数3,3<=5,且3>2,更新v=[2,3];处理第六个数7,7>3,插入v尾部,v=[2,3,7];处理下一个数101,101>7,插入尾部,更新v=[2,3,7,101];处理下一个数18,18<=101,且18>7(找出18更新的位置,即更新大于等于18,且最接近18的数),更新v=[2,3,7,18]。结束。主意:一个数如果大于v的尾部,则插入尾部;否则更新v,该数更新的位置为大于等于该数且最接近该数的数。这样的话,一个数插入复杂度为1,更新复杂度为logn。这样,总的复杂度为O(nlogn)。


代码如下(c++,O(nlogn),可AC):

int binarysearch(vector<int>& v, int x){//二分查找出x更新的位置
        int left=0, right=v.size()-1, mid=0;
        while(left<right){
            mid = (left+right)/2;
            if(x==v[mid])
                return mid;
            else if(x>v[mid])
                left=mid+1;
            else if(x<v[mid])
                right=mid;
        }
        return right;
    }


    int lengthOfLIS(vector<int>& nums) {
        if(nums.size()<=1)  return nums.size();
        
        vector<int> v;//最长递增序列
        v.push_back(nums[0]);
        for(int i=1;i<nums.size();i++){
            if(nums[i]>v.back()){  //nums[i]大于数组中最大数,则插入尾部
                v.push_back(nums[i]);
            }
            else{  //将nums[i]更新到已有递增序列
                int idx =binarysearch(v,nums[i]);
                v[idx] = nums[i];
            }
        }

        return v.size();
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值