最长递增子序列(力扣和牛客版本)-C++版本分享

程序员养生小课堂:
最近项目二期+写paper+做毕设焦头烂额,每天坐在办公室12h以上,腱鞘囊肿与颈椎劳损并发,由于久坐二郎腿+盘腿膝盖也不太好了,于是这两天都25min番茄钟就会逼迫自己站起来拉伸一下臀部肌肉和大腿前侧肌肉,扭扭腰,或者拉伸一下肩颈部的肌肉,b站上很多5分钟小课程,这两天明显感觉疲惫感减轻了,晚上入睡的时候也不会头疼了,总之站起来走走,哪哪都有好处

首先,LeetCode的最长递增子序列要求求出长度,官方解法的贪心+二分,时间复杂度是O(nlogn),空间复杂度O(n)。

class Solution {
public:
    /**
     * retrun the longest increasing subsequence
     * @param arr int整型vector the array
     * @return int整型vector
     */
    vector<int> LIS(vector<int>& arr) {
        // write code here
        int n = arr.size();
        vector<int> d(n+1); //长度为index时,最长递增子序列的末尾元素
        d[1] = arr[0];
        int len = 1;
        for(int i =1 ; i < n ; i++){
            if(arr[i] > d[len]){
                //当前遍历值,比当前递增子序列末尾还大,放到++len的位置
                d[++len] = arr[i];
            }
            else{
                //当前遍历比当前长度递增子序列的末尾小,其有可能和前面的小数构成递增序列
                //找到d的一个位置,这个位置的后面一个位置,是一个比它大的,前面的,都比i位置的数小
                //利用二分法来找
                int left = 1 , right = len;
                int pos = 0; //记录,最后一个比arr[i]小的位置,arr[i]会放在pos+1的地方
                while(left <= right){ 
                    int mid = left + (right - left)/2;
                    if(d[mid] < arr[i]){
                        left = mid + 1;
                        pos = mid;
                    }
                    else{
                        right = mid - 1;
                    }
                }
                d[pos + 1] = arr[i];
            }
        }
        
		return len;
    }
};

直接返回len,len中保存的就是最终的递增子序列长度,具体算法思路已经在备注里了。

牛客版本的需要做一些额外处理,也就是在获得当前最长递增子序列时,要把位置记下来

class Solution {
public:
    /**
     * retrun the longest increasing subsequence
     * @param arr int整型vector the array
     * @return int整型vector
     */
    vector<int> LIS(vector<int>& arr) {
        // write code here
        int n = arr.size();
        vector<int> index(n);//index[i]表示当前长度的最后一个数的索引为i
        vector<int> d(n+1); //长度为index时,最长递增子序列的末尾元素
        d[1] = arr[0];
        index[0] = 1;
        int len = 1;
        
        for(int i =1 ; i < n ; i++){
            if(arr[i] > d[len]){
                //当前遍历值,比当前递增子序列末尾还大,放到++len的位置
                d[++len] = arr[i];
                index[i] = len; //第i个位置为len的末尾元素
            }
            else{
                //当前遍历比当前长度递增子序列的末尾小,其有可能和前面的小数构成递增序列
                //找到d的一个位置,这个位置的后面一个位置,是一个比它大的,前面的,都比i位置的数小
                //利用二分法来找
                int left = 1 , right = len;
                int pos = 0; //记录,最后一个比arr[i]小的位置,arr[i]会放在pos+1的地方
                while(left <= right){ 
                    int mid = left + (right - left)/2;
                    if(d[mid] < arr[i]){
                        left = mid + 1;
                        pos = mid;
                    }
                    else{
                        right = mid - 1;
                    }
                }
                d[pos + 1] = arr[i];
                index[i] = pos + 1;
            }
        }
        
        //d[len] 最长的长度是 len
        vector<int> ans(len);
        for(int i = n - 1 ; i >= 0 ; i--){
        //考虑当i从后往前遍历,这个遍历顺序确保了其【子序列】特性
            if(index[i] ==  len){
            //对应找到len长度的递增子序列末尾对应的i所在
                ans[--len] = arr[i];
            }
        }
        return ans;
    }
};

两个坑:

  1. 当我把int mid = left + (right - left)/2;写成int mid = left + (right - left) >> 1;时,会超时。
    自行试验后,Xcode会如下提醒,结果也是不正确的
    在这里插入图片描述

Operator ‘>>’ has lower precedence than ‘+’; ‘+’ will be evaluated first

  1. 当我把while(left <= right){写成while(left < right){时,会错误。

这个问题是二分中非常常见的问题,考虑这个问题的时候,我们假设left == right,此时 mid == left == right,那么如果最后d[mid]>=arr[i],皆大欢喜。但是如果d[mid]<arr[i],由于while循环早就早left==right的时候跳出了,所以pos记录的位置就不对了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值