跳到主要内容

300. 最长递增子序列

mid

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

示例 1:

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。

示例 2:

输入:nums = [0,1,0,3,2,3]
输出:4

示例 3:

输入:nums = [7,7,7,7,7,7,7]
输出:1

DP

func lengthOfLIS(nums []int) int {
res := 1
dp := make([]int, len(nums))
dp[0] = 1
for i := 1; i < len(nums); i++ {
maxLen := 0
for j := 0; j < i; j++ {
if nums[j] < nums[i] && dp[j] > maxLen {
maxLen = dp[j]
}
}
dp[i] = maxLen + 1
if dp[i] > res {
res = dp[i]
}
}
return res
}
image-20220811120126565

DP + 二分

很具小巧思。新建数组 dp,用于保存最长上升子序列。

对原序列进行遍历,将每位元素二分插入 dp 中。

  • 如果 dp 中元素都比它小,将它插到最后
  • 否则,用它覆盖掉比它大的元素中最小的那个。

总之,思想就是让 dp 中存储比较小的元素。这样,dp 未必是真实的最长上升子序列,但长度是对的。

func lengthOfLIS(nums []int) int {
if len(nums) == 1 {
return 1
}
dp := []int{nums[0]}
for i := 1; i < len(nums); i++ {
// nums[i] 最大
if nums[i] > dp[len(dp)-1] {
dp = append(dp, nums[i])
continue
}
low, high := 0, len(dp)-1
for low <= high {
mid := low + (high-low)>>1
if nums[i] <= dp[mid] {
if mid==0||nums[i] > dp[mid-1] {
// 找到第一个大于等于nums[i]的元素
dp[mid] = nums[i] // 替换
break
}
high = mid - 1
} else {
low = mid + 1
}
}
}
return len(dp)
}

这样时间复杂度能降低到 O(nlogn)O(nlogn)

DFS

超时了,他妈的

func lengthOfLIS(nums []int) int {
res := 1
var dfs func(startIndex, lastVal, curLen int)
dfs = func(startIndex, lastVal, curLen int) {
if curLen > res {
res = curLen
}
if startIndex == len(nums) {
return
}
for i := startIndex; i < len(nums); i++ {
if nums[i] > lastVal && curLen+len(nums)-i > res {
dfs(i+1, nums[i], curLen+1)
}
}
}
for i := 1; i < len(nums); i++ {
dfs(i, nums[i-1], 1)
}
return res
}