zhangdizhangdi

300. 最长递增子序列 🟨 🌟

题目

LeetCode 中等

题目

给你一个整数数组 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

题解

方法:动态规划

  • 时间复杂度:O(n2)
  • 空间复杂度:O(n)
js
function lengthOfLIS(nums) {
  const len = nums.length
  if (len === 0) return 0
  if (len === 1) return 1
  let maxLen = 1
  const dp = Array(len).fill(1)
  for (let i = 1; i < len; i++) {
    console.log('♻️ i:', i, 'nums[i]:', nums[i])
    for (let j = 0; j < i; j++) {
      console.log('j:', j, 'nums[j]:', nums[j])

      //与前面元素比较
      if (nums[i] > nums[j]) {
        //碰到小的,dp[j]+1
        dp[i] = Math.max(dp[j] + 1, dp[i])
        maxLen = Math.max(maxLen, dp[i])
      }
      console.log('dp:', dp)
    }

    console.log('------')
  }

  console.log('dp:', dp)

  return maxLen
}

const array = [3, 5, 6, 2, 5, 4]
console.log('🌰', lengthOfLIS(array))
执行结果
♻️ i: 1 nums[i]: 5
j: 0 nums[j]: 3
dp: [1,2,1,1,1,1]
------
♻️ i: 2 nums[i]: 6
j: 0 nums[j]: 3
dp: [1,2,2,1,1,1]
j: 1 nums[j]: 5
dp: [1,2,3,1,1,1]
------
♻️ i: 3 nums[i]: 2
j: 0 nums[j]: 3
dp: [1,2,3,1,1,1]
j: 1 nums[j]: 5
dp: [1,2,3,1,1,1]
j: 2 nums[j]: 6
dp: [1,2,3,1,1,1]
------
♻️ i: 4 nums[i]: 5
j: 0 nums[j]: 3
dp: [1,2,3,1,2,1]
j: 1 nums[j]: 5
dp: [1,2,3,1,2,1]
j: 2 nums[j]: 6
dp: [1,2,3,1,2,1]
j: 3 nums[j]: 2
dp: [1,2,3,1,2,1]
------
♻️ i: 5 nums[i]: 4
j: 0 nums[j]: 3
dp: [1,2,3,1,2,2]
j: 1 nums[j]: 5
dp: [1,2,3,1,2,2]
j: 2 nums[j]: 6
dp: [1,2,3,1,2,2]
j: 3 nums[j]: 2
dp: [1,2,3,1,2,2]
j: 4 nums[j]: 5
dp: [1,2,3,1,2,2]
------
dp: [1,2,3,1,2,2]
🌰 3

方法:贪心 + 二分查找

  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(n)

❗️ 得到的 tail 并不一定是最后正确的序列,只能保证 length 正确。

js
function lengthOfLIS(nums) {
  const len = nums.length
  if (len <= 1) {
    return len
  }
  const tail = [nums[0]]
  for (let i = 0; i < len; i++) {
    const num = nums[i]

    console.log('-------')
    console.log(num)

    if (num > tail[tail.length - 1]) {
      //当nums中的元素比tail中的最后一个大时,直接push
      tail.push(num)
    } else {
      let li = 0 //左下标
      let ri = tail.length - 1 //右下标
      while (li < ri) {
        let mi = (li + ri) >> 1 //中间下标

        console.log('li', li, 'ri', ri, 'mi', mi, '| tailNum', tail[mi])

        if (tail[mi] < num) {
          li = mi + 1
        } else {
          ri = mi
        }
      }
      tail[li] = num //将 num 放置到合适的位置,此时前面的元素都比 num 小
    }

    console.log(`第${i + 1}轮:`, JSON.stringify(tail))
  }
  return tail.length
}

const array = [3, 5, 6, 2, 5, 4, 19, 5, 6, 7, 12]
console.log('🌰', lengthOfLIS(array))
执行结果
-------
3
第1轮: [3]
-------
5
第2轮: [3,5]
-------
6
第3轮: [3,5,6]
-------
2
li 0 ri 2 mi 1tailNum 5
li 0 ri 1 mi 0tailNum 3
第4轮: [2,5,6]
-------
5
li 0 ri 2 mi 1tailNum 5
li 0 ri 1 mi 0tailNum 2
第5轮: [2,5,6]
-------
4
li 0 ri 2 mi 1tailNum 5
li 0 ri 1 mi 0tailNum 2
第6轮: [2,4,6]
-------
19
第7轮: [2,4,6,19]
-------
5
li 0 ri 3 mi 1tailNum 4
li 2 ri 3 mi 2tailNum 6
第8轮: [2,4,5,19]
-------
6
li 0 ri 3 mi 1tailNum 4
li 2 ri 3 mi 2tailNum 5
第9轮: [2,4,5,6]
-------
7
第10轮: [2,4,5,6,7]
-------
12
第11轮: [2,4,5,6,7,12]
🌰 6