递归生成子集时直接推入tmp数组得空结果的原因解析
子集递归代码中
tmp与tmp.slice()的差异原因 这段递归求数组子集的代码里,终止条件用res.push(tmp)会输出全空数组,换成res.push(tmp.slice())就能得到正确结果,核心原因是JavaScript里数组是引用类型,具体拆解如下:
- 数组
tmp属于引用类型,程序中所有用到tmp的地方,指向的都是内存里同一个数组实例。递归过程中我们一直在对这个实例做push和pop操作,本质是持续修改同一个数组。 - 直接执行
res.push(tmp)时,res里存储的是这个数组实例的引用,而非当前tmp的内容快照。等整个递归流程走完,tmp会经过多次回溯的pop操作,最终回到初始的空数组状态,此时res里所有元素指向的都是这个空数组,打印出来自然全是空的。 - 而
tmp.slice()会创建一个全新的数组副本,把当前tmp的所有元素复制到这个新数组里,再把新数组的引用存入res。后续对原tmp的修改不会影响到这些已经存入res的副本,所以最终res里保存的是各个递归终止时刻的子集内容,结果完全正确。
举个执行片段的例子:当递归走到pos=3时,tmp是[1,2,3],如果用res.push(tmp),res里存的是这个数组的引用;之后回溯时执行tmp.pop()删掉3,tmp变成[1,2],此时res里的第一个元素也会跟着变成[1,2],直到最后tmp被清空,res里所有元素都变成空数组。但用slice()的话,存的是[1,2,3]的副本,后续tmp怎么修改都不会影响这个副本。
内容的提问来源于stack exchange,提问作者WangXM
相关产品推荐
相关产品推荐

