Skip to content

力扣链接:300.最长递增子序列

难度:⭐⭐

✏️ 方法一:更推荐方法二

解题关键词:动态规划

解题思路:每走一个元素,然后用当前元素和前面所有元素比一下,如果比前面元素大,那么说明可以链接到某个元素最长子序列中,不断找最大的。

typescript
function lengthOfLIS(nums: number[]): number {
  // 存放最大值
  let result = 1;
  const length = nums.length;
  // 初始化一个 dp 数组,存放到每个元素最长的递增子序列长度
  const dp = new Array(length).fill(1);

  for (let i = 1; i < length; i++) {
    // 前面走过的,遍历一下
    for (let j = 0; j < i; j++) {
      // 如果当前这个元素比前面元素大,那么更新一下最大值
      if (nums[i] > nums[j]) {
        dp[i] = Math.max(dp[j] + 1, dp[i]);
        result = Math.max(dp[i], result);
      }
    }
  }

  return result;
}

✏️ 方法二:

解题关键词:贪心二分查找

解题思路:从左往右遍历 nums,维护一个 dp 数组(单调递增的),里面存放着最长子序列(可能顺序不是正确的),如果当前遍历到的数字比数组中最后一个大,将当前数字加入数组中,如果比最后一个数字小,那么要找到数组中比它自身小的最后一个元素,然后将找到的pos位置后面一个替换为当前数字。在查找比指定元素小的最后一个元素时,使用二分查找,因为数组是单调递增的。

typescript
function lengthOfLIS(nums: number[]): number {
  if (!nums.length) return 0;
  // 存放最长子增序列,可能不是正确的
  const dp = [nums[0]];

  for (let i = 1; i < nums.length; i++) {
    // 如果当前元素比数组中最后一个元素大,加入数组
    if (nums[i] > dp[dp.length - 1]) {
      dp.push(nums[i]);
    } else {
      // 找到了比指定元素小的数组中最后一个元素
      const targetIndex = findIndex(dp, nums[i]);
      // 将后面元素替换掉
      dp.splice(targetIndex + 1, 1, nums[i]);
    }
  }

  return dp.length;
}

// 二分查找最后一个比 target 元素小的元素位置
function findIndex(nums: number[], target: number) {
  // 默认为 -1,如果 pos 返回 -1,代表所有元素都比 target 大,这个时候要替换数组中第一个位置,因为上面会 +1,所以这里默认为 -1
  let pos = -1;
  let left = 0;
  let right = nums.length - 1;

  while (left <= right) {
    const mid = Math.floor((right - left) / 2) + left;

    if (nums[mid] < target) {
      pos = mid;
      left = mid + 1;
    } else {
      right = mid - 1;
    }
  }

  return pos;
}