Vue3中求这个最长递增子序列用的是贪心算法+二分查找
- 当前这一项比最后一项大则直接放到末尾
- 如果当前这一项比最后一项小,需要在序列中通过二分查找找到比当前大的这一项,用他来替换掉
- 最优的情况就是默认递增
注意:
因为我们的这个算法所传过来的数组是上篇说到的记录的是老的虚拟节点中间部分元素与新的虚拟节点中间元素进行patch对比时的记录,这个数组处理默认填充的都是0,有元素进行了比对,才会把值更改为该元素在老的元素中的下标,所以这个数组为0的情况是没有意义的,为0的项也会被直接删除掉,所以我们写这个算法需要排除数组中为0的项
循环数组,拿到每一项的值,过滤0
取结果集中的最后一项,如果当前项比结果集最后一项所对应的值大,那么直接将当前索引push结果集的最后
如果当前项比结果集最后一项所对应的值小,那么我们就通过二分查找,在结果集中找到比当前值大的,用当前值的索引将其替换掉
- 拿到结果集中间的值所对应的数组中的值,跟当前项进行对比,如果当前项大于中间值,就找后半部分,否则找前半部分
如果二分查找最后找到的值比当前的值大,那么我们就把结果集中当前的值替换成当前下标
注意:
至此,我们求出来的结果集的个数就是我们要找的最长递增子序列的个数,但也只是个数对,内容不一定的对,因为顺序不一定对,因为如果这个数组的后面的数比前边的小,会进行替换,但是替换后,在原数组的顺序就是不正确的了。所以我们至此只找对了个数
问题:那我们怎么把值弄正确呢?
答案是利用索引做前驱节点追溯,在每次放入或者取代结果集里项的时候,让当前放入的索引对应的值记住它前一个值对应的索引,如果是替换操作,那么记住被替换的值所对应的索引,如果是插入操作,那么直接记住结果集最后一项值。有点绕,其实内容不对的原因就是由于贪婪算法时替换引起的,但是我们这种记住前一个,在替换操作时,记住是被替换的前一个,这样你即使替换后,记住的前一个也是正确的。然后我们从最后一个开始,根据它们的前驱节点一个一个进行追溯,这个最后得出的结果顺序是逆的,我们再转下顺序,根据每一项去原数组里找到对应值,最后再得出的结果就是我们要找的最长递增子序列。只能追溯找的原因是最后一项一定是正确的。
思路清晰后,我们来进行实现
我们先设置一个跟原数组一样长的数组来作追溯
我们在第一种情况push后,让放进的项记住前一项
在第二种情况替换操作时,找到被替换项的前一项记录下来
最后我们就得出了一个追溯列表,开始向前追溯
- 先拿到最长递增子序列的正确个数
- 然后找到最后最后一项,这一定是正确的
- 然后循环把result中的值按照正确的回溯列表进行更新
- 最后我们就得出了最长递增子序列的下标数组
应用到vue3乱序比对中
- 传入的数组是我们上篇文章讲到的乱序比对中,用一个数组来记录新老虚拟节点的中间部分是否比对过
在新老虚拟节点对比过后,进行移动逻辑中,循环新虚拟节点时,如果元素被对比过,更改逻辑
- 如果当前新虚拟节点该元素下标与求得的最长递增子序列该项不一致,我们就进行插入,也就是元素移动,否则就不动,减少插入次数
总结:
因为在乱序比对中,一开始的逻辑,在遍历新虚拟节点中间部分时,判断元素只要被比对过,就移动元素进行插入操作,其实那些按照递增的元素的位置是可以不用动的,把之外被比对过需要移动插入的元素进行移动,一定程度上优化了性能