vue3最长自增子序列算法注释版

728 阅读1分钟
function getSequence(arr) {
  /*
  回溯时使用  遍历arr的时候 每操作一次result push或者替换值
  都把result被操作索引的前一项放到p  比如 result = [0] 要push i进去result前 p数组肯定会记录当前result的最后一项 也就是0(p[i]=0) 
  回溯时通过最后一项(result[result.length - 1])  就可以通过P这个数组 一直找到他的前一项(p[result[result.length - 1]])
  */
  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) {
      // 找result最后一项 也就是目前为止最大的一项  如果比这项大 就直接push
      j = result[result.length - 1]
      if (arr[j] < arrI) {
        //这里可以看出 result记录了i 通过这个i 又可以从p中取出result前一项
        p[i] = j
        result.push(i)
        continue
      }
      // 否则在前面的项中二分查找 并且当前项在p中的位置记录result列表的最后一项 也就是说通过push进去的这一项(索引) 在p中对应的索引存就是它的前一项
      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] = j 一样 都是记录前一项
          p[i] = result[u - 1]
        }
        result[u] = i
      }
    }
  }

  // 回溯
  /*
    为什么需要回溯?
    [3,4,1] 根据上面的逻辑  1会把3替换掉 导致最长递增子序列索引变为[2,1] 显然不对
    因为result是递增插入的 所以在被二分查找元素替换前  result的结果一定是对的
    每次替换元素的时候 该元素的后一项元素 都在p中记录了它的位置
    所以回溯法可以找回正确的递增序列
    */
  u = result.length
  v = result[u - 1]
  while (u-- > 0) {
    // 第一次循环 v就是result里面的最后一项 因为最后一项是最大值 肯定是对的
    result[u] = v
    // 最后一项在p中记录了它的前一项 所以取出前一项放在result
    // ♻️ 每取出一项 都能在p中找到它的前一项
    v = p[v]
  }
  return result
}