vue3中的算法:最长递增子序列

60 阅读2分钟

vue3的diff算法使用了最长递增子序列,目的是找出最多的不需要移动的节点,以达到节省性能的目的,如果你还不清楚vue3的diff算法,请移步这里

leetcode中类似题目

leetcode中有一道类似的题目:300. 最长递增子序列:给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
这道题相对简单,因为只需要计算最长递增子序列的长度,我们先来解决这道问题

解法一:动态规划

动态规划是相对容易想到的一种做法,状态我们可以定义为:dp[i]是以i位置元素结尾的最长递增子序列长度,初始化状态dp[i]初始值都为1,状态转移方程是:dp[i] = i位置之前所有小于i对应元素中的最大dp值 + 1,所以代码实现:

function lengthOfLIS(nums: number[]): number {
  const n = nums.length;
  // 初始化状态 记录i为结尾的最长递增子序列长度
  const dp = new Array(n).fill(1);
  let max: number = 1;
  // 状态转移方程
  for(let i = 1; i < n; i++) {
    for(let j = 0; j < i; j++) {
      if (nums[j] < nums[i]) {
        dp[i] = Math.max(dp[j] + 1, dp[i])
      }
    }
    max = Math.max(dp[i], max)
  }
  return max;
};

动态规划的时间复杂度达到了O(n²),效率不算高。

解法二:贪心 + 二分查找

思路:维护一个数组tails,用于记录扫描到的元素应该存放的位置;扫描原数组中的每一个元素,在tails数组中找是否存在一个num,比当前扫描元素大,且num左侧的值都比当前扫描元素小,如果有,当前扫描元素替代num的位置,如果没有,直接放到tails数组尾部;结束扫描后tails数组就是最长递增子序列,它的长度也就是最长递增子序列的长度,来具体实现:

function lengthOfLIS(nums: number[]) {
  const len = nums.length;
  const tails: number[] = [];
  for (let i = 0; i < len; i++) {
    const num = nums[i];
    // 二分查找
    let left = 0,
      right = tails.length - 1;
    while(left <= right) {
      const mid = Math.floor((left + right) / 2);
      if (num <= tails[mid]) {
        right = mid - 1
      } else {
        left = mid + 1
      }
    }
    if (left === tails.length) {
      // 如果left指针等于tails长度,说明num比tails里所有元素都要大
      tails.push(num)
    } else {
      // 反之 说明left位置的元素比num大 left左边的元素比num小
      tails[left] = num
    }
  }
  return tails.length;
}

该方法的时间复杂度是O(nlogn),相对于动态规划有非常大的提升。