Discuss: www.cnblogs.com/grandyang/p…
Given an integer array nums
, return the length of the longest strictly increasing subsequence.
A 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
}
}