最长上升子序列解法

161 阅读1分钟

引言

在某一次面试的时候,面试官出了一道求最长上升子序列的算法题,身为算法菜鸡当然无法在短短十分钟内解出来。不过,知耻而后勇,下来肯定要查漏补缺,说干就干!

暴力解法

时间复杂度:O(2ⁿ) ,因为每个数据都有两种可能性(排列组合)

直接穷举所有的可能性,不过有“爆栈”的风险,并不推荐哈

/**
* @param {number[]} nums
* @return {number}
*/
function lengthOfLIS(nums) {
  const temp = [...nums]
  if (temp.length === 0) {
    return 0
  }
  const target = []
  for(let i = 0; i < temp.length; i++) {
    const len = target.length
    // 每次外层循环
    for(let j = 0; j < len; j++) {
      const curArr = target[j]
      const lastIndex = curArr.length - 1
      // 加入该队列
      if (curArr[lastIndex] < temp[i]) {
        target.push([...curArr, temp[i]])
      }
    }
    // 前面几次都不选
    target.push([temp[i]])
  }
  let maxLen = 0
  for(let m = 0; m < target.length; m++) {
    if (target[m].length > maxLen) {
      maxLen = target[m].length
    }
  }
  return maxLen
}

动态规划

时间复杂度:O(n²) ,两次循环

使用动态规划来解决,主要是找到状态转移方程,当前状态可有前面状态推导出来

/**
* @param {number[]} nums
* @return {number}
*/
function lengthOfLIS(nums) {
  if (nums.length <= 1) {
    return nums.length
  }
  const dp = [1]
  let maxLen = 0
  const len = nums.length
  for(let i = 1; i < len; i++) {
    let max = 0
    for(let j = i - 1; j >= 0; j--) {
      if (nums[j] < nums[i]) {
        // 根据前面状态推导当前状态
        max = Math.max(dp[j], max)
      }
    }
    dp[i] = max + 1
    maxLen = Math.max(maxLen, dp[i])
  }
  return maxLen
}

贪心+二分

时间复杂度:O(nlogn) ,一次循环加一次二分查找

具体方案简述:用栈结构,如果值比栈内所有值都大则入栈,否则替换比它大的最小数,最后栈的长度就是结果

/**
* @param {number[]} nums
* @return {number}
*/
function lengthOfLIS(nums) {
  const len = nums.length
  if (len === 0) {
    return 0
  }
  let curLen = 1
  const temp = []
  temp[curLen] = nums[0]
  for(let i = 1; i < len; i++) {
    if (nums[i] > temp[curLen]) {
      temp[++curLen] = nums[i]
    } else {
      // 二分查找temp[index],它是比nums[i]大的最小数
      let low = 1, high = curLen, index = 0
      while(low <= high) {
        let mid = Math.floor((high - low) / 2) + low
        if (temp[mid] < nums[i]) {
          index = mid
          low = mid + 1
        } else {
          high = mid - 1
        }
      }
      temp[index + 1] = nums[i]
    }
  }
  temp.shift()
  return curLen
}

总结

在Vue的diff算法中为了减少元素的移动次数,使用了贪心+二分查找算法实现。虽然最后结果总长度是对的,但是内容不匹配,需要额外的操作才能找到正确的序列