🎃🎃🎃小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
🎯 是这样子的,鄙人正在肝一道算法题,也就是经典的最长重复子数组的类似版本最长重复子串
鄙人关于此类算法问题的总结->算法:子串、子序列相关问题小结 - 掘金 (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()这个方法虽然用起来简便,人畜无害的样子;但是但是,它对于引用类型,填充的是引用地址,用的时候一定要注意!
好了,吃一堑长一智,今天又是长智慧的一天呢!🍳