LeetCode 300. Longest Increasing Subsequence 最长递增子序列

187 阅读1分钟

leetcode.com/problems/lo…

Discuss: www.cnblogs.com/grandyang/p…

Given an integer array nums, return the length of the longest strictly increasing subsequence.

subsequence is a sequence that can be derived from an array by deleting some or no elements without changing the order of the remaining elements. For example, [3,6,2,7] is a subsequence of the array [0,3,1,6,2,2,7].

 

Example 1:

Input: nums = [10,9,2,5,3,7,101,18]
Output: 4
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.

Example 2:

Input: nums = [0,1,0,3,2,3]
Output: 4

Example 3:

Input: nums = [7,7,7,7,7,7,7]
Output: 1

 

Constraints:

  • 1 <= nums.length <= 2500
  • -104 <= nums[i] <= 104

 

Follow up: Can you come up with an algorithm that runs in O(n log(n)) time complexity?

解法一:

使用动态规划求解。这道题让我们求最长递增子串 Longest Increasing Subsequence 的长度,简称 LIS 的长度。首先来看一种动态规划 Dynamic Programming 的解法,这种解法的时间复杂度为 O(n2),类似 brute force 的解法,维护一个一维 dp 数组,其中 dp[i] 表示以 nums[i] 为结尾的最长递增子串的长度,对于每一个 nums[i],从第一个数再搜索到i,如果发现某个数小于 nums[i],更新 dp[i],更新方法为 dp[i] = max(dp[i], dp[j] + 1),即比较当前 dp[i] 的值和那个小于 num[i] 的数的 dp 值加 1 的大小,就这样不断的更新 dp 数组,到最后 dp 数组中最大的值就是要返回的 LIS 的长度,参见代码如下:

class Solution {
    fun lengthOfLIS(nums: IntArray): Int {
        var result = 1
        val length = nums.size
        val dp = IntArray(size = length, init = { 1 })

        for (i in 1 until length) {
            for (j in 0 until i) {
                if (nums[j] < nums[i]) {
                    dp[i] = Math.max(dp[i], dp[j] + 1)
                }
            }
            result = Math.max(result, dp[i])
        }
        return result
    }
}

解法二:

下面来看一种优化时间复杂度到 O(nlgn) 的解法,这里用到了二分查找法,所以才能加快运行时间。思路是,先建立一个数组 lis,把首元素放进去,然后比较之后的元素,如果遍历到的新元素比 lis 数组中的首元素小的话,替换首元素为此新元素,如果遍历到的新元素比 lis 数组中的末尾元素还大的话,将此新元素添加到 lis 数组末尾(注意不覆盖原末尾元素)。如果遍历到的新元素比 lis 数组首元素大,比尾元素小时,此时用二分查找法找到第一个不小于此新元素的位置,覆盖掉位置的原来的数字,以此类推直至遍历完整个 nums 数组,此时 lis 数组的长度就是要求的 LIS 的长度,特别注意的是 lis 数组的值可能不是一个真实的 LIS,比如若输入数组 nums 为 {4, 2, 4, 5, 3, 7},那么算完后的 ends 数组为 {2, 3, 5, 7},可以发现它不是一个原数组的 LIS,只是长度相等而已,千万要注意这点。参见代码如下:

class Solution {
    fun lengthOfLIS(nums: IntArray): Int {
        val lis = mutableListOf<Int>()
        lis.add(nums[0])
        for (i in 1 until nums.size) {
            if (nums[i] > lis[lis.lastIndex]) {
                lis.add(nums[i])
            } else if (nums[i] < lis[0]) {
                lis[0] = nums[i]
            } else {
                var left = 0
                var right = lis.size
                var mid: Int
                while (left < right) {
                    mid = (left + right) / 2
                    if (lis[mid] < nums[i]) {
                        left = mid + 1
                    } else {
                        right = mid
                    }
                }
                lis[right] = nums[i]
            }
        }
        return lis.size
    }
}