引言
在某一次面试的时候,面试官出了一道求最长上升子序列的算法题,身为算法菜鸡当然无法在短短十分钟内解出来。不过,知耻而后勇,下来肯定要查漏补缺,说干就干!
暴力解法
时间复杂度: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算法中为了减少元素的移动次数,使用了贪心+二分查找算法实现。虽然最后结果总长度是对的,但是内容不匹配,需要额外的操作才能找到正确的序列