Daily-Interview-Question icon indicating copy to clipboard operation
Daily-Interview-Question copied to clipboard

第 97 题:React 和 Vue 的 diff 时间复杂度从 O(n^3) 优化到 O(n) ,那么 O(n^3) 和 O(n) 是如何计算出来的?

Open yygmind opened this issue 6 years ago • 34 comments

yygmind avatar Jul 01 '19 15:07 yygmind

好问题:fist:

yingye avatar Jul 02 '19 08:07 yingye

我理解的是:react对比按照树的层级去对比,给树节点编号0,1,2,3...,相同编号对比,不一样就直接删除然后,添加新节点,

Mohannnnn avatar Jul 02 '19 09:07 Mohannnnn

react只做了同层比较,同级的节点类型不同就认为不同,重新渲染。

rocky-191 avatar Jul 02 '19 13:07 rocky-191

我认为react做了三种优化来降低复杂度:1:如果父节点不同,放弃对子节点的比较,直接删除旧节点然后添加新的节点重新渲染;2:如果子节点有变化,Virtual DOM不会计算变化的是什么,而是重新渲染,3:通过唯一的key策略

Jenear avatar Jul 05 '19 03:07 Jenear

我知道,但是我就是不说

azl397985856 avatar Jul 05 '19 13:07 azl397985856

我知道,但是我就是不说

调皮

chenzesam avatar Jul 09 '19 14:07 chenzesam

知乎:https://www.zhihu.com/question/66851503

从一棵树转化为另外一棵树,直观的方式是用动态规划,通过这种记忆化搜索减少时间复杂度。由于树是一种递归的数据结构,因此最简单的树的比较算法是递归处理。确切地说,树的最小距离编辑算法的时间复杂度是 O(n^2m(1+logmn)), 我们假设 m 与 n 同阶, 就会变成 O(n^3)。

libin1991 avatar Jul 10 '19 02:07 libin1991

Dads

原始邮件 发件人: Zhilin[email protected] 收件人: Advanced-Frontend/Daily-Interview-Question[email protected] 抄送: Subscribed[email protected] 发送时间: 2019年7月10日(周三) 18:37 主题: Re: [Advanced-Frontend/Daily-Interview-Question] 第 97 题:React 和 Vue 的 diff 时间复杂度从 O(n^3) 优化到 O(n) ,那么 O(n^3) 和 O(n) 是如何计算出来的? (#151)

我知道,但是我就是不说

你真坏 死鬼

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHubhttps://github.com/Advanced-Frontend/Daily-Interview-Question/issues/151?email_source=notifications&email_token=AE2P5FJGNTWVJXOEOVPP7DLP6W3YHA5CNFSM4H4TKCU2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODZTB2BY#issuecomment-510008583, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AE2P5FMB4TIKAG22GM222J3P6W3YHANCNFSM4H4TKCUQ.

nano-papa avatar Jul 10 '19 10:07 nano-papa

谈一下理解: 主要看了vue 的diff 算法: O(n) 代表如果有n节点需要更新,只需要操作dom n 次就能完成。 但是这里有个前提是 这n个节点更新后和原来dom 要在同层,如果跨层更新节点,肯定比O(n)复杂。 至于O(n^3)怎么来的不是很清楚。。。 参考的文章:https://github.com/aooy/blog/issues/2

bobohuochai avatar Jul 11 '19 02:07 bobohuochai

关于O(n^3)怎么计算出来的

问题描述

原问题标题“React 和 Vue 的 diff 时间复杂度从 O(n^3) 优化到 O(n) ,那么 O(n^3) 和 O(n) 是如何计算出来的? ”

  1. 这里的n指的是页面的VDOM节点数,这个不太严谨。如果更严谨一点,我们应该应该假设 变化之前的节点数为m,变化之后的节点数为n。

  2. React 和 Vue 做优化的前提是“放弃了最优解“,本质上是一种权衡,有利有弊。

倘若这个算法用到别的行业,比如医药行业,肯定是不行的,为什么?

React 和 Vue 做的假设是:

  • 检测VDOM的变化只发生在同一层
  • 检测VDOM的变化依赖于用户指定的key

如果变化发生在不同层或者同样的元素用户指定了不同的key或者不同元素用户指定同样的key, React 和 Vue都不会检测到,就会发生莫名其妙的问题。

但是React 认为, 前端碰到上面的第一种情况概率很小,第二种情况又可以通过提示用户,让用户去解决,因此 这个取舍是值得的。 没有牺牲空间复杂度,却换来了在大多数情况下时间上的巨大提升。 明智的选择!

基本概念

首先大家要有个基本概念。

其实这是一个典型的最小编辑距离的问题,相关算法有很多,比如Git中 ,提交之前会进行一次对象的diff操作,就是用的这个最小距离编辑算法。

leetcode 有原题目, 如果想明白这个O(n^3), 可以先看下这个。

对于树,我们也是一样的,我们定义三种操作,用来将一棵树转化为另外一棵树:

  • 删除:删除一个节点,将它的children交给它的父节点

  • 插入:在children中 插入一个节点

  • 修改:修改节点的值

事实上,从一棵树转化为另外一棵树,我们有很多方式,我们要找到最少的。

直观的方式是用动态规划,通过这种记忆化搜索减少时间复杂度。

算法

由于树是一种递归的数据结构,因此最简单的树的比较算法是递归处理。

详细描述这个算法可以写一篇很长的论文,这里不赘述。 大家想看代码的,这里有一份 我希望没有吓到你。

确切地说,树的最小距离编辑算法的时间复杂度是O(n^2m(1+logmn)), 我们假设m 与 n 同阶, 就会变成 O(n^3)

题外话

大家如果对数据结构和算法感兴趣,可以关注下我的leetcode题解

azl397985856 avatar Jul 11 '19 03:07 azl397985856

React 从来没有说过 “React 比原生操作 DOM 快”。React 的基本思维模式是每次有变动就整个重新渲染整个应用。如果没有 Virtual DOM,简单来想就是直接重置 innerHTML。很多人都没有意识到,在一个大型列表所有数据都变了的情况下,重置 innerHTML 其实是一个还算合理的操作... 真正的问题是在 “全部重新渲染” 的思维模式下,即使只有一行数据变了,它也需要重置整个 innerHTML,这时候显然就有大量的浪费。 我们可以比较一下 innerHTML vs. Virtual DOM 的重绘性能消耗:

  • innerHTML: render html string O(template size) + 重新创建所有 DOM 元素 O(DOM size)。

  • Virtual DOM: render Virtual DOM + diff O(template size) + 必要的 DOM 更新 O(DOM change)。

Virtual DOM render + diff 显然比渲染 html 字符串要慢,但是!它依然是纯 js 层面的计算,比起后面的 DOM 操作来说,依然便宜了太多。可以看到,innerHTML 的总计算量不管是 js 计算还是 DOM 操作都是和整个界面的大小相关,但 Virtual DOM 的计算量里面,只有 js 计算和界面大小相关,DOM 操作是和数据的变动量相关的。前面说了,和 DOM 操作比起来,js 计算是极其便宜的。

这才是为什么要有 Virtual DOM:它保证了 1)不管你的数据变化多少,每次重绘的性能都可以接受;2) 你依然可以用类似 innerHTML 的思路去写你的应用。

作者:尤雨溪 链接:https://www.zhihu.com/question/31809713/answer/53544875 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

15957787988 avatar Jul 11 '19 14:07 15957787988

这个问题做算法分析的都很难在面试的时候说明白的。不知道哪家公司会问这种问题。

jrt8 avatar Jul 21 '19 05:07 jrt8

我知道,但是我就是不说

臭弟弟,宁可别给我逮着了

xuzelin1 avatar Jul 22 '19 06:07 xuzelin1

关于O(n^3)怎么计算出来的

问题描述

原问题标题“React 和 Vue 的 diff 时间复杂度从 O(n^3) 优化到 O(n) ,那么 O(n^3) 和 O(n) 是如何计算出来的? ”

  1. 这里的n指的是页面的VDOM节点数,这个不太严谨。如果更严谨一点,我们应该应该假设 变化之前的节点数为m,变化之后的节点数为n。
  2. React 和 Vue 做优化的前提是“放弃了最优解“,本质上是一种权衡,有利有弊。

倘若这个算法用到别的行业,比如医药行业,肯定是不行的,为什么?

React 和 Vue 做的假设是:

  • 检测VDOM的变化只发生在同一层
  • 检测VDOM的变化依赖于用户指定的key

如果变化发生在不同层或者同样的元素用户指定了不同的key或者不同元素用户指定同样的key, React 和 Vue都不会检测到,就会发生莫名其妙的问题。

但是React 认为, 前端碰到上面的第一种情况概率很小,第二种情况又可以通过提示用户,让用户去解决,因此 这个取舍是值得的。 没有牺牲空间复杂度,却换来了在大多数情况下时间上的巨大提升。 明智的选择!

基本概念

首先大家要有个基本概念。

其实这是一个典型的最小编辑距离的问题,相关算法有很多,比如Git中 ,提交之前会进行一次对象的diff操作,就是用的这个最小距离编辑算法。

leetcode 有原题目, 如果想明白这个O(n^3), 可以先看下这个。

对于树,我们也是一样的,我们定义三种操作,用来将一棵树转化为另外一棵树:

  • 删除 删除一个节点,将它的children交给它的父节点
  • 插入 在children中 插入一个节点
  • 修改 修改节点的值

事实上,从一棵树转化为另外一棵树,我们有很多方式,我们要找到最少的。

直观的方式是用动态规划,通过这种记忆化搜索减少时间复杂度。

算法

由于树是一种递归的数据结构,因此最简单的树的比较算法是递归处理。

详细描述这个算法可以写一篇很长的论文,这里不赘述。 大家想看代码的,这里有一份 我希望没有吓到你。

确切地说,树的最小距离编辑算法的时间复杂度是O(n^2m(1+logmn)), 我们假设m 与 n 同阶, 就会变成 O(n^3)

题外话

大家如果对数据结构和算法感兴趣,可以关注下我的leetcode题解

你吓到我了

NathanHan1 avatar Jul 23 '19 04:07 NathanHan1

diff策略 React用 三大策略 将O(n^3)复杂度 转化为 O(n)复杂度 策略一(tree diff): Web UI中DOM节点跨层级的移动操作特别少,可以忽略不计。 策略二(component diff): 拥有相同类的两个组件 生成相似的树形结构, 拥有不同类的两个组件 生成不同的树形结构。 策略三(element diff): 对于同一层级的一组子节点,通过唯一id区分。

webdazhuo avatar Nov 11 '19 16:11 webdazhuo

没看懂你们的回答在答什么,原来的O(n^3)是怎样的,然后通过什么策略变成了O(n)的?

No3ming avatar Jan 12 '20 09:01 No3ming

这个问题做算法分析的都很难在面试的时候说明白的。不知道哪家公司会问这种问题。

昨晚我就被字节问了,问的很深

BertieGo avatar Jun 17 '20 11:06 BertieGo

没看懂你们的回答在答什么,原来的O(n^3)是怎样的,然后通过什么策略变成了O(n)的?

问题只是问3次方怎么计算,没有说 n 怎么优化的哦

azl397985856 avatar Jun 18 '20 02:06 azl397985856

话说回来 react 的 diff 应该是 O(2n) 吧,第一次 n 是新树一对一节点对比老树,第二次 n 是应用 patches ....

BertieGo avatar Jun 22 '20 01:06 BertieGo

没看懂你们的回答在答什么,原来的O(n^3)是怎样的,然后通过什么策略变成了O(n)的?

原来的 O(n^3) 的 diff 流程是:

  • 老树的每一个节点都去遍历新树的节点,直到找到新树对应的节点。那么这个流程就是 O(n^2),再紧接着找到不同之后,再计算最短修改距离然后修改节点,这里是 O(n^3)

BertieGo avatar Jun 22 '20 01:06 BertieGo

个人猜想:

  1. 对于两棵树的比较,最简单的就是遍历,两层嵌套就是 O(n^2)
  2. 假设每个节点都要做更新操作,那就是再加个 O(n)。加起来就是 O(n^3)

vue的更新策略就是:深度优先、同层比较。就是只比较同层级,也就是 O(n)

kaixinfu avatar Jul 10 '20 02:07 kaixinfu

这个问题做算法分析的都很难在面试的时候说明白的。不知道哪家公司会问这种问题。

海康威视

yuerdev avatar Jul 27 '20 15:07 yuerdev

  1. 同一层级节点进行比较
  2. 节点的标签是否相同,相同不再进行比较,不同直接删除重新创建节
  3. 节点的标签和key是否相同, 相同不再进行比较,不同直接删除重新创建节点

seakeys avatar Oct 27 '20 03:10 seakeys

这个问题做算法分析的都很难在面试的时候说明白的。不知道哪家公司会问这种问题。

火花思维有问到

macc6579 avatar Apr 28 '21 03:04 macc6579

React 和 Vue 做的假设是:

  • 检测VDOM的变化只发生在同一层
  • 检测VDOM的变化依赖于用户指定的key

@azl397985856 根据 key 来进行比较,这个过程的复杂度不是 O(k1 * k2) 的吗 (k1, k2 分别为新旧子元素列表长度);即便使用 Map / Hash 优化,也应该是 O(k1 log(k2)),好像也达不到 O(N) 的复杂度

guanghechen avatar Jun 27 '21 01:06 guanghechen

我知道,但是我就是不说

你真坏

打你小屁屁

wangShengCool avatar Jul 08 '21 03:07 wangShengCool

为了降低算法复杂度,React的diff会预设三个限制:

只对同级元素进行Diff。如果一个DOM节点在前后两次更新中跨越了层级,那么React不会尝试复用他。

两个不同类型的元素会产生出不同的树。如果元素由div变为p,React会销毁div及其子孙节点,并新建p及其子孙节点。

开发者可以通过 key prop来暗示哪些子元素在不同的渲染下能保持稳定。

wangShengCool avatar Jul 08 '21 03:07 wangShengCool

我也被问到了,滴滴

evilpose avatar Aug 14 '21 15:08 evilpose

O(n^3)是指两棵树diff时:

  1. 遍历第一棵树,在第一棵树的遍历中再去遍历第二颗树,找出各个节点diff出的结果并记录到一个数组中,这里头操作的时间复杂度是O(n^2)
  2. 遍历diff记录的数组,并对第一棵树的diff位置做增删改操作,时间复杂度O(n)

O(n^3) = O(n) * O(n^2)得出,涉及了跨层级对比计算出的结果。

而React优化后的O(n^2)只做了同级diff,如果同级(位置)diff不同,则删除原节点再重新生成一个新节点,所以时间复杂度是O(n)

johnsoncheg avatar Apr 29 '22 02:04 johnsoncheg