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),相对于动态规划有非常大的提升。