别看了 别看了,都是浅拷贝惹的祸

512 阅读3分钟

🎃🎃🎃小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

🎯 是这样子的,鄙人正在肝一道算法题,也就是经典的最长重复子数组的类似版本最长重复子串

鄙人关于此类算法问题的总结->算法:子串、子序列相关问题小结 - 掘金 (juejin.cn)

我们都知道,在这一类算法当中,我们需要利用动态规划来构建一个二维数组记录匹配的个数,也就是像这样👇👇👇

[0, 0, 0, 0, 0, 0, 0, 0]
[0, 1, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 1, 0, 0, 0, 0, 0]
[0, 0, 0, 2, 0, 0, 0, 0]
[0, 0, 0, 0, 3, 0, 0, 0]
[0, 0, 0, 0, 0, 4, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]

那么首先,就要先初始化一个全为0的二维数组,这个时候一定要注意了,千万不要像鄙人一样用以下的方法🔨

错误尝试

方式1:

const dp = new Array(str1.length + 1).fill(new Array(str2.length + 1).fill(0))

鄙人懒,竟然想用一句代码来解决(问题就出现了,下面会解释是啥问题)🦵🦵

方式2:

const dp = []
let row = []
for(let j = 0;j<str2.length + 1;j++){
  row.push(0)
}
for(let i = 0;i<str1.length + 1;i++){
  dp.push(row)
}

问题出现

当你使用这两种方法的时候,你会发现,当你想要对dp某一项进行改变的时候,其它项会跟随着一起改变,就像这样:

const dp = new Array(str1.length + 1).fill(new Array(str2.length + 1).fill(0))
dp[0][0] = 1
console.log(dp)

打印出来,你想象当中应该是:🙄

[1, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]

实际上却是😥

[1, 0, 0, 0, 0, 0, 0, 0]
[1, 0, 0, 0, 0, 0, 0, 0]
[1, 0, 0, 0, 0, 0, 0, 0]
[1, 0, 0, 0, 0, 0, 0, 0]
[1, 0, 0, 0, 0, 0, 0, 0]
[1, 0, 0, 0, 0, 0, 0, 0]
[1, 0, 0, 0, 0, 0, 0, 0]
[1, 0, 0, 0, 0, 0, 0, 0]
[1, 0, 0, 0, 0, 0, 0, 0]
[1, 0, 0, 0, 0, 0, 0, 0]

这就导致了,明明我的代码逻辑没有错,但是结果却大相径庭😱😱

原因分析

鄙人最初百思不得其解。吃过饭后,突然拍脑袋一想!一个改变,其它跟着改变,这不是在浅拷贝的时候经常讲的话?

哎呀,这不就是浅拷贝在作祟嘛!😲

无论是第一种方法还是第二种方法,dp当中的每一项都是一个数组对象的引用,并没有实现真正意义上的多维数组

想明白之后,鄙人用了下面的方法:

const dp = new Array(str1.length + 1);
let max = 0;
for (let i = 0; i <= str1.length; i++) {
  dp[i] = new Array(str2.length + 1).fill(0);  //传入的时候,每一项都重新new一个新数组
} 

这个时候,就可以出现我们想要的结果了✨

总结

这其实是一个很典型的浅拷贝的问题,所以当我们再遇到这种改变一个引用类型,其它引用类型跟着一起变化的时候,首先就从浅拷贝这里下手;

同时我也突然发现Array.fill()这个方法虽然用起来简便,人畜无害的样子;但是但是,它对于引用类型,填充的是引用地址,用的时候一定要注意!

好了,吃一堑长一智,今天又是长智慧的一天呢!🍳