返回

LeetCode: JavaScript中的最长递增子序列 - DP+二分

前端

导言

在计算机科学领域,最长递增子序列(LIS)问题是一个经典而重要的问题。给定一个序列,我们需要找出其中最长的递增子序列,即一个子序列满足其元素按升序排列。LIS问题在实际应用中非常广泛,例如在优化算法、数据挖掘和生物信息学等领域。

解决思路

解决LIS问题的常用方法有两种:动态规划(DP)和二分查找。

动态规划

动态规划是一种自底向上的解决问题的技术,将问题分解成一系列子问题,然后通过递推的方式解决每个子问题,最终得到问题的整体解。在LIS问题中,我们可以定义状态dp[i]表示以元素i结尾的最长递增子序列的长度。然后我们可以通过递推的方式计算出dp[i]的值:

dp[i] = max(dp[j] + 1) for all j < i and nums[j] < nums[i]

其中max(dp[j] + 1)表示在以元素j结尾的最长递增子序列上添加元素i后,可以得到一个更长的递增子序列。

二分查找

二分查找是一种高效的搜索算法,可以快速找到一个有序序列中的指定元素。在LIS问题中,我们可以使用二分查找来找到以元素i结尾的最长递增子序列的长度。具体做法是,首先将所有元素排序,然后对于每个元素i,我们在有序序列中寻找一个最小的元素j,使得nums[j] < nums[i]。那么,以元素i结尾的最长递增子序列的长度就是j + 1。

JavaScript实现

// 动态规划
const longestIncreasingSubsequenceDP = (nums) => {
  const n = nums.length;
  const dp = new Array(n).fill(1);
  for (let i = 1; i < n; 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);
};

// 二分查找
const longestIncreasingSubsequenceBS = (nums) => {
  const n = nums.length;
  const dp = new Array(n).fill(-1);
  let len = 0;
  for (let i = 0; i < n; i++) {
    const index = binarySearch(dp, 0, len, nums[i]);
    dp[index] = nums[i];
    len = Math.max(len, index + 1);
  }
  return len;
};

const binarySearch = (dp, left, right, target) => {
  while (left < right) {
    const mid = Math.floor((left + right) / 2);
    if (dp[mid] < target) {
      left = mid + 1;
    } else {
      right = mid;
    }
  }
  return left;
};

总结

本文通过详细的讲解和示例,帮助读者理解了LeetCode中最长递增子序列问题,并提供了两种经典的解决方法:动态规划和二分查找。同时,我们还提供了详细的JavaScript实现代码,帮助读者掌握这一经典问题,并为其他类似问题打下坚实基础。