【Leetcode 300最长子序列】最优解的总结

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

示例:

输入: [10,9,2,5,3,7,101,18]
输出: 4 
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4

以上来自leetcode链接:https://leetcode-cn.com/problems/longest-increasing-subsequence/

这题的难点在于在O(nlogn)的时间复杂度解决它,这个题解给的方法解释都很难懂,看了很久以及看了好几个题解,才勉强理解,所以记录一下自己理解以后自己总结。

1.首先,看到这种题目想到的算法解决办法肯定是动态规划。

2.解题的主要算法思路是:建立一个保存子序列的DP数组,一个指针向前遍历,如果元素比子序列动态数组里最后一个元素都大,那就保存在后面,如果小于,那就在数组里找到那个比它大的数(最接近的)替换掉。

首先,这样一个算法思路保持着动态数组一直是单调的。简单证明:因为这样的添加元素方式一直是保持有序的。

其次,这样的思路原因是由于贪心算法,尽量先找一个小的元素保存下来,保持序列前面的元素尽量小,这样才可以让后面序列尽量长。然后查找元素的时候使用二分查找,就可以降低复杂度O(n)到O(logn),因为是单调有序的才可以使用二分查找。

最后,为什么这种算法思路可以奏效,一开始看肯定会很奇怪,就算知道使用了贪心思路。我们使用示例输入来模拟一下:

[10]->[9]->[2]->[2,5]->[2,3]->[2,3,7]->[2,3,7,101]->[2,3,7,18]

可以看到的是,每次都将较小的保留下来,能构成子序列的话,小的那个数是肯定能替代大的那个数的功能的,所以会尽量让子序列的元素小一点。

最后放出自己写的代码,可以互相参考。

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        vector<int> LIS_min;
        if(nums.size()<=1) return nums.size();
        LIS_min.push_back(nums[0]);
        for(int i =0;i<nums.size();i++){
            if(nums[i]>LIS_min[LIS_min.size()-1]){
                LIS_min.push_back(nums[i]);
            }
            else{
                int left = 0;
                int right = LIS_min.size()-1;
                int mid;
                while(left<right){
                    mid = (left+right)/2;
                    if(LIS_min[mid]==nums[i]) { left = mid; break; }
                    else if(LIS_min[mid]<nums[i]) left = mid+1;
                    else right = mid;
                }
                LIS_min[left] = nums[i];
            }
        }
        return LIS_min.size();
    }
};

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值