LeetCode 300

https://leetcode.cn/problems/longest-increasing-subsequence/description/

难度:中等

本题求最长上升子序列(LIS)有两种做法,分别是动态规划和贪心。

动态规划:

f[i] 表示以 i 结尾的 LIS。

f[i] = max(f[j]) + 1, j < i && nums[j] < nums[i]

时间复杂度:\(O(n^2)\),其中 n 为 nums 的长度。

空间复杂度:O(n)。

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int n = nums.size();
        vector<int> f(n, 0);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < i; j++) {
                if (nums[j] < nums[i]) {
                    f[i] = max(f[i], f[j]);
                }
            }
            f[i]++;
        }
        return *max_element(f.begin(), f.end());
    }
};

贪心:

g[i] 长为 i + 1 的上升子序列的末尾元素最小值。对于 nums 中的每一个元素 x,如果在 g 中可以找到比他大的元素,那么将这个元素更新为 x。如果 x 比所有元素都大,那么把 x 加到 g 的末尾,子序列长度加一。这个查找的过程可以用二分查找来实现。

时间复杂度:O(nlogn),其中 n 为 nums 的长度。

空间复杂度:O(n)。

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        vector<int> g;

        auto lower_bound = [&](int x) {
            int left = -1, right = g.size();
            while (left + 1 < right) {
                int mid = left + (right - left) / 2;
                if (g[mid] >= x) {
                    right = mid;
                } else {
                    left = mid;
                }
            }
            return right;
        };

        for (int x: nums) {
            int idx = lower_bound(x);
            if (idx == g.size()) {
                g.push_back(x);
            } else {
                g[idx] = x;
            }
        }
        return g.size();
    }
};