leetcode 300 最长上升子序列 (贪心+二分解法)

题目描述
链接 B站视频讲解

在之前的解法中运用的是动态规划,其时间复杂度为O(n2)。使用贪心和二分可以将时间复杂度降低。贪心的策略是:

  • 首先定义两个数组,res保存最长递增子序列,mlen保存以元素 i 为结尾的最长递增子序列的长度
  • 初始化时两个数组,res保存给定数组的第一个元素,mlen保存元素1,表示此时的长度为1.
  • 遍历数组中其余的元素,如果遍历到的元素大于res数组最后一个元素,那么此时表明是递增,需要将该数字加入到res数组中,mlen加入以该元素为结尾的序列的长度,也就是res数组的长度加入到mlen 中。如果遍历到的元素小于res最后一个元素,此时需要在res数组中找第一个比arr[i]大的元素,将该元素替换为arr[i],由于已经找到了arr[i] 在res 中的位置,说明该位置代表了以 arr[i] 结尾的元素的长度,实际长度需要该下标加1,将该值加入到mlen数组中。
  • 这样做是为了保存有更大的概率能够续接后面的元素。我认为res数组中保存的是多个递增子序列的一个合集,将这些子序列按照递增顺序排列好,每个位置取这一列的最小值。比如:递增序列集合为{[1,3],[1,2,4,5],[1,2,3]} 按照列合并得到res数组为[1,2,3,5]
  • 注意此时res保存的并不是最后字典序最小的元素,需要另行寻找。在寻找过程中找的是最后一次出现相应长度的位置。比如给定arr=[1,2,8,6,4] 手工看到的结果是[1,2,4],选择mlen的下标为0,1,4;是不是与原始数组选择最长递增子序列的下标是一致的。原始数组选择下标为0,1,4即可得到[1,2,4]
res数组mlen数组
11
1,21,2
1,2,81,2,3
1,2,61,2,3,3
1,2,41,2,3,3,3
  • 用二分寻找第一个大于该元素的位置可以使用lower_bound(res.begin(), res.end(), arr[i]) - res.begin();
class Solution {
public:
    int findmax(vector<int>& num,int k)
    {
        int l=0,r=num.size()-1;
        while(l<r)
        {
            int mid=l+r>>1;
            if(num[mid]<k) l=mid+1;
            else r=mid;
        }
        return l;
    }
    vector<int> LIS(vector<int>& arr) {
        // write code here
        vector<int> res;
        vector<int> mlen;
        res.emplace_back(arr[0]);
        mlen.emplace_back(1);
        int n=arr.size();
        for(int i=1;i<n;i++)
        {
            if(arr[i]>res.back())
            {
                res.push_back(arr[i]);
                mlen.emplace_back(res.size());//注意不是mlen.back()+1,例如mlen数组[1,2,3,1,3]
            }
            else
            {
                int pos=findmax(res,arr[i]);
                res[pos]=arr[i];
                mlen.emplace_back(pos+1);
            }
        }
        for (int i = arr.size()-1, j = res.size(); j > 0; i--) {
            if (mlen[i] == j) {
                res[--j] = arr[i];
            }
        }
        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值