300. 最长上升子序列(Longest Increasing Subsequence)M

英文题目

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

  • Example:

    Output: 4 
    Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4. 
    
  • Note:

    • 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?

中文题目

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

  • 示例:

    输出: 4 
    解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
    
  • 说明:

    • 可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
    • 你算法的时间复杂度应该为 O(n2) 。
  • 进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?

暴力法

  • 使用「回溯搜索」可得到输入数组的所有子序列,时间复杂度是O(2^N),再对这些子串一次判定是否「严格上升」,时间复杂度是O(N),所以总的时间复杂度是O(N * 2^N)

动态规划

  • dp[i] 表示前 i 个元素,以第 i 个数字结尾的最长上升子序列的长度,注意 nums[i] 必须被选取

  • 有两种情况:

    • 对于 nums[j] 和 nums[i] (j < i),
    • 1.对于 nums[j] 和 nums[i] (j<i),使得 nums[j]≤nums[i] 且 dp[j]+1>dp[i](即把 nums[i] 跟在以 nums[j] 结尾的LIS后面时能比当前以 nums[i] 结尾的LIS长度更长),那么就把 nums[i] 跟在以 nums[j] 结尾的LIS后面,形成一条更长的不下降子序列(遍历每一个在 i 前面的元素 j ,符合条件的令 dp[i]=dp[j]+1,会把最后最长的一个累加起来)
    • 2.如果 nums[i] 之前的元素都大于 nums[i],那么 nums[i]只好自己形成一条LIS,但是长度为1,即这个子序列里面只有一个 nums[i]
  • 状态转移方程:dp[i]=max{1, dp[j]+1}(j=1,2,⋯,i-1 && nums[j]<nums[i])

  • 时间复杂度O(n^2),空间复杂度O(n)

    # python3: 时间 2872 ms, 击败 39.81%; 内存 16.3 MB, 击败 49.50%
    class Solution:
        def lengthOfLIS(self, nums: List[int]) -> int:
            dp = [1] * len(nums)
            for i in range(len(nums)):
                for j in range(i):
                    if nums[i] > nums[j]:
                        dp[i] = max(dp[i], dp[j]+1)
            return max(dp)
    
    // c++: 时间 316 ms, 击败 16.75%; 内存 10.2 MB, 击败 44.7%
    class Solution {
    public:
        int lengthOfLIS(vector<int>& nums) {
            vector<int> dp(nums.size(), 1);
            for (int i = 0; i < nums.size(); ++i) {
                for (int j = 0; j < i; ++j) {
                    if (nums[i] > nums[j]) {
                        dp[i] = max(dp[i], dp[j]+1);
                    }
                }
            }
            return *max_element(dp.begin(), dp.end());
        }
    };
    
    // java: 时间 61 ms, 击败 53.38%; 内存 41.8 MB, 击败 48.51%
    class Solution {
        public int lengthOfLIS(int[] nums) {
            int[] dp = new int[nums.length];
            Arrays.fill(dp, 1);
            for (int i = 0; i < nums.length; ++i) {
                for (int j = 0; j < i; ++j) {
                    if (nums[i] > nums[j]) {
                        dp[i] = Math.max(dp[i], dp[j]+1);
                    }
                }
            }
            return Arrays.stream(dp).max().getAsInt();
        }
    }
    
    // go: 时间 56 ms, 击败 60.42%; 内存 3.6 MB, 击败 9.8%
    func lengthOfLIS(nums []int) int {
        dp := make([]int, len(nums))
        for i := range(dp) {
            dp[i] = 1
        }
        for i := 0; i < len(nums); i++ {
            for j := 0; j < i; j++ {
                if nums[i] > nums[j] {
                    dp[i] = max(dp[i], dp[j]+1)
                }
            }
        }
        return maxSlice(dp)
    }
    
    func max(i, j int) int {
        if i > j {
            return i
        }
        return j
    }
    
    func maxSlice(nums []int) int {
        res := nums[0]
        for _, num := range nums {
            if res < num {
                res = num
            }
        }
        return res
    }
    
    // javascript: 时间 176 ms, 击败 51.82%; 内存 42.6 MB, 击败 57.5%
    /**
     * @param {number[]} nums
     * @return {number}
     */
    var lengthOfLIS = function(nums) {
        const dp = new Array(nums.length).fill(1);
        for (let i = 0; i < nums.length; ++i) {
            for (let j = 0; j < i; ++j) {
                if (nums[i] > nums[j]) {
                    dp[i] = Math.max(dp[i], dp[j]+1);
                }
            }
        }
        return Math.max(...dp);
    };
    
Last Updated:
Contributors: Shiqi Lu