最长递增子序列

本文介绍了如何使用动态规划解决最长递增子序列问题,时间复杂度为O(n^2),并通过引入二分查找进行优化,将时间复杂度降低到O(nlogn)。给出了两种方法的详细实现代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

最长递增子序列(Longest Increasing Subsequence,简称LIS)是在一个数列中找到一个最长的子序列(不一定连续),使得这个子序列中的所有元素都是按照严格递增的顺序排列的。例如,在数列 [10, 9, 2, 5, 3, 7, 101, 18] 中,最长递增子序列是 [2, 3, 7, 101],其长度为4。

思路:

1.动态规划法

动态规划的基本思想是,
对于数列中的每一个元素,我们记录下以该元素结尾的最长递增子序列的长度。然后,对于每一个新的元素,我们检查在它之前的元素,找到一个最长的递增子序列,该序列可以通过添加这个新元素来延长。这样,我们就可以构建出一个最长递增子序列。

动态规划解法的时间复杂度是 O(n^2),其中 n 是数列的长度。

使用动态规划方法:

function lengthOfLIS(nums) {
  if (nums.length === 0) return 0;
  let dp = new Array(nums.length).fill(1);
  for (let i = 1; 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);
}

这段代码会创建一个数组dp,其中dp[i]表示以nums[i]结尾的最长递增子序列的长度。对于数组中的每个元素,它都会查找在它之前的所有元素,找到可以形成递增子序列的最长长度,并更新dp[i]。最后,它返回dp数组中的最大值,即整个数组的最长递增子序列的长度。

2. 二分查找法

使用二分查找来优化最长递增子序列(LIS)的解法是一种效率更高的方法,可以将时间复杂度从 O(n^2) 降低到 O(n \log n)。这种方法结合了贪心算法和二分查找,其核心思想是维护一个数组,该数组在任何时刻都保存着当前找到的所有递增子序列的最小可能尾数。
以下是这种优化方法的步骤:

  1. 初始化一个空数组 tails,用于保存最小尾数。
  2. 遍历原始数组,对于每个元素执行以下操作:
    • 使用二分查找在 tails 中找到第一个大于或等于当前元素的位置。
    • 如果找到这样的位置,用当前元素替换该位置的值(这意味着存在一个更小的值作为相同长度的递增子序列的尾数)。
    • 如果没有找到这样的位置,将当前元素添加到 tails 的末尾(这意味着我们找到了一个更长的递增子序列)。
  3. 遍历结束后,tails 数组的长度即为最长递增子序列的长度。

下面是JavaScript中实现这一优化方法的代码示例:

function lengthOfLIS(nums) {
  let tails = [];
  for (let x of nums) {
    let i = 0, j = tails.length;
    while (i != j) {
      let m = (i + j) >> 1;
      if (tails[m] < x)
        i = m + 1;
      else
        j = m;
    }
    tails[i] = x;
    if (i === tails.length) tails.push(x);
  }
  return tails.length;
}

这段代码通过维护一个有序数组 tails,并使用二分查找来优化查找过程,从而提高了算法的效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值