【LeetCode从零单刷】Longest Increasing Subsequence

题目:

Given an unsorted array of integers, find the length of longest increasing subsequence.

For example, Given [10, 9, 2, 5, 3, 7, 101, 18],
The longest increasing subsequence is [2, 3, 7, 101], therefore the length is 4. Note that there may be more than one LIS combination, it is only necessary for you to return the length.

Your algorithm should run in O(n2) complexity.

Follow up: Could you improve it to O(n log n) time complexity?

解答:

寻找最长上升子序列(LIS),最基本的方法是动态规划。转移方程为 dp[i] = max{dp[j]} + 1 ( -1< j < i && nums[j] < nums[i])。

解释也非常直观:dp[i] 表示如果取得当前 i 元素,所能达到的最长 LIS。如果需要打印出此序列,仅需要保存上一个元素。

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int size = nums.size();
        if (size <= 1)  return size;
        
        vector<int> dp(size, 1);
        int maxlen;
        for (int i = 1; i < size; i++) {
            maxlen = 0;
            for (int j = 0; j < i; j++) {
                if (nums[j] < nums[i] && maxlen < dp[j]) maxlen = dp[j];
            }
            dp[i] = maxlen + 1;
        }
        
        maxlen = 0;
        for (int i = 0; i < size; i++) {
            if (dp[i] > maxlen) maxlen = dp[i];
        }
        return maxlen;
    }
};

如果要修改到 O(n log n) time complexity?贪心法 + 二分搜索

增加一条辅助的顺序(ordered)栈(队列……完成任务就好),保存尽可能长的LIS。入栈的要求为:

  1. 将a[0]入栈
  2. 每次取栈顶元素top(最大元素)和读到的元素a[i](0<i<n)做比较:
  3. - 如果a[i] > top 则将a[i]入栈;
  4. - 如果a[i] <= top则二分查找栈中的比a[i]大的第1个数,并用a[i]替换它(如果栈顶元素被替换,说明有机会到达更长序列);
  5. 最长序列长度即为栈的大小

直观理解:对于 x 和 y,如果 x < y 且 stack[y] < stack[x],用 stack[x] 替换 stack[y],此时的最长序列长度没有改变,但序列继续变长的''潜力''增大,这就是贪心的目标。

举例:原序列为1,5,8,3,6,7

开始1,5,8相继入栈,此时读到3,用3替换5,得到1,3,8;再读6,用6替换8,得到1,3,6;再读7,得到最终栈为1,3,6,7。最长递增子序列为长度4

但是这个方法,有一个很大的缺陷:只能保证序列长度的正确性,不能保证栈中就是正确的序列

举例:原序列为1,5,8,2,栈内最后是 1,2,8 不是正确的序列。

分析一下,我们可以看出,虽然有些时候这样得不到正确的序列,但最后算出来的个数是没错的,为什么呢?

当a[i]>top时,总个数直接加1,这肯定没错;但当a[i]<top时呢? 这时a[i] 会替换栈里面的某一个元素,总个数不变。如果求具体序列,还是动态规划比较好。

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        vector<int> LIS;
        for (int i = 0; i < nums.size(); i++) {
            if (LIS.size() == 0 || LIS[LIS.size() - 1] < nums[i]) {
                LIS.push_back(nums[i]);
            }
            else {
                int l = 0, r = LIS.size() - 1;
                while (l < r) {
                    int m = (l + r) / 2;
                    if (LIS[m] >= nums[i]) {
                        r = m;
                    }
                    else {
                        l = m + 1;
                    }
                }
                LIS[l] = nums[i];
            }
        }
        return LIS.size();
    }
};

参考资料:

https://en.wikipedia.org/wiki/Longest_increasing_subsequence

http://blog.csdn.net/yorkcai/article/details/8651895

https://leetcode.com/discuss/67888/c-clean-o-nlogn-code-using-binary-search

你可能感兴趣的:(LeetCode,C++,LIS,最长上升子序列)