[leetcode] Longest Increasing Subsequence

Longest Increasing Subsequence

  • 问题描述:计算一个数组的最长上升子序列。这个子序列内的元素不一定要是相邻的。比如说数组{1, 100, 2, 3, 4}的LIS就为{1, 2, 3, 4}
  • 解法1-动态规划
    • 首先我们将问题划分成子问题=>我们该序列一定是以某一个点为起点的。所有我们另dp[i]表示以i为起点的LIS长度。
    • 构造父问题的解。假设说我们已经知道了dp[i],那么我们如何构造dp[i-1]呢?注意,这里我们构造的是dp[i-1],而不是dp[i+1].针对dp[i-1]的所能得到的所有结果就是,nums[i-1] < nums[j] 所有j点中的最大值+1或者就是本身的dp[i-1] 即忽略该点。
    • 总结下来dp[i] = max(dp[i], dp[j]+1) j ∈ [ i + 1 , n )     a n d     n u m s [ j ] > n u m s [ i ] j\in[i+1, n)\ \ \ and\ \ \ nums[j] > nums[i] j[i+1,n)   and   nums[j]>nums[i]
    • 代码:
int lengthOfLISV2(vector<int>& nums) {
        int size = (int) nums.size();
        if(size == 0)
            return 0;
        int dp[size]; // dp[i]表示以i开头的位置
        memset(dp, 0, sizeof(dp));
        int res = 0;
        for(int i=size-1;i>=0;i--){
            for(int j=i+1;j<size;j++){
                if(nums[i] <= nums[j]){
                    dp[i] = max(dp[i], dp[j] + 1);
                    res = max(res, dp[i]);
                }
            }
        }
        return res + 1;
    }
  • 解法2:O(NlogN):
    • 参照1, 2
    • 我们新建一个数组B,保存可能是最后subsequence的数字。
    • 我们遍历输入数组A中的每个数组,针对该数字a,我们在B中找到小于它的最大数字
      • 如果B中所有数字都小于a,则我们直接将a加入B中,代表我们从开始的点,到当前数字a能生成的LIS长度一定会增加1。
      • 如果B中所有数字都大于a, 则我们B中的第一个数字置换成a,代表我们可能会从新开始一个新的subsequence。⚠️我们并没有改变B的大小。所有最后的LIS的长度不会改变。
      • 如果a在B中间,我们将B中最大的小于a的元素替换成a。道理和上一种情况是一样的。
    • 最后B的大小,就是我们所能返回LIS的长度。
    • 概括来说,当由SubArray[:i-1]构造SubArray[:i]的解时候,如果nums[i]在SubArray[:i-1]对应的Subsequence之间的话,则我们把其中最大的小于nums[i]的替换成nums[i]。因为原来的数字已经没有用了,它起的作用完全可以由nums[i]代替,所以我们可以将其替换掉。
    • 代码
int lengthOfLISV3(vector<int>& nums) {
        vector<int> saved_num;
        for(auto n: nums){
            auto it = lower_bound(saved_num.begin(), saved_num.end(), n);
            if(it == saved_num.end()){
                saved_num.push_back(n);
            }else{
                *it = n;
            }
        }
        return saved_num.size();
    }

你可能感兴趣的:(ACM/算法)