Vue3虚拟DOM计算diff——最长递增子序列

194 阅读1分钟

源码

为了获取最优的最长递增子序列,根据leetcode 300. 最长递增子序列获取的并不一定是排序最小的序列也可通过,而贪心(其实就像数组[10,9,2,5,3,7,101,18],[2,3]肯定比[2,5]好) + 二分法,使获得的上升幅度最小,序列最长。

方法所在库传送门:github.com/vuejs/core/…

// https://en.wikipedia.org/wiki/Longest_increasing_subsequence
function getSequence(arr: number[]): number[] {
  const p = arr.slice()
  const result = [0]
  let i, j, u, v, c
  const len = arr.length
  for (i = 0; i < len; i++) {
    const arrI = arr[i]
    if (arrI !== 0) {
      j = result[result.length - 1]
      if (arr[j] < arrI) {
        p[i] = j
        result.push(i)
        continue
      }
      u = 0
      v = result.length - 1
      while (u < v) {
        c = (u + v) >> 1
        if (arr[result[c]] < arrI) {
          u = c + 1
        } else {
          v = c
        }
      }
      if (arrI < arr[result[u]]) {
        if (u > 0) {
          p[i] = result[u - 1]
        }
        result[u] = i
      }
    }
  }
  u = result.length
  v = result[u - 1]
  while (u-- > 0) {
    result[u] = v
    v = p[v]
  }
  return result
}

分析

function getLongestIncreasingSubsequence(arr) {
  // 复制一份数组,用于存储序列索引
  let p = arr.slice();
  let result = [0]; // 最长递增子序列(存储的索引下标)
  let ai, ri, left, right, mid;
  for (ai = 0; ai < arr.length; ai++) {
    const arrItem = arr[ai];
    if (arrItem !== 0) {
      // 拿【序列索引】最大值
      ri = result[result.length - 1];
      // 大于【序列索引下最大值】直接添加
      if (arr[ri] < arrItem) {
        p[ai] = ri; // 存储【序列索引】到【复制的数组】下
        result.push(ai);
        continue;
      }

      // 当前循环值小于【序列索引下最大值】,需要把其插入到序列内
      // 二分法
      left = 0;
      right = result.length - 1;
      // 得到当前循环值所在【序列最小值】的位置
      while (left < right) {
        mid = (left + right) >> 1; // 二分下标值
        if (arr[result[mid]] < arrItem) {
          // 数组值大于序列二分值,left向右移(二分下标值+1)个位置,继续二分
          left = mid + 1;
        } else {
          // 否则,right左移到二分下标值,继续二分
          right = mid;
        }
      }

      // 二分定位的下标值left 可能大于等于数组值,也可能小于数组值

      // 数组值小于left值,则存储序列的第(left - 1)位的下标
      if (arrItem < arr[result[left]]) {
        // left值必须要大于0
        if (left > 0) {
          // 将【序列(left - 1)索引】存于【复制的数组】下
          // 建立和 left 位的索引
          p[ai] = result[left - 1];  // 这时ai坐标位的值就应该是对应上一个序列索引
        }
        result[left] = ai;
      }
    }
  }
  left = result.length;
  right = result[left - 1]; //取最后一个值,其当前已是最长序列,但不一定是最长递增值最优序列
  while (left-- > 0) {
    // 倒序根据在数组p里的索引添加
    result[left] = right;
    right = p[right]; // 获取存储在p里的序列索引值
  }
  return result;
}