哈哈,昨天晚上debug三个小时,到晚上快11点还没搞明白是哪儿出了问题,今早一起来脑子清醒点了,一眼就看出问题所在:这喜欢捉弄人的引用传递和值传递。
talk is cheap, show me your code
先上代码:
const wordSquares = (words) => {
const n = words[0].length;
let results = [], squares = [];
const buildTrie = (words) => {
let root = {};
for(let i = 0; i < words.length; i++){
let node = root;
for(const c of words[i]){
if(!node[c]) node[c] = {"#": []};
node = node[c];
node["#"].push(i); //?
}
}
return root;
};
let trie = buildTrie(words);
const getWordsWithPrefix = (prefix) => {
let node = trie;
for(const c of prefix){
if(!node[c]) return [];
node = node[c];
}
let ret = [];
for(const index of node["#"]){
ret.push(words[index]);
}
return ret;
};
const backtracking = (step, squares) => {
if(step == n){
results.push(squares);
/* console.log(results); */
return;
}
let prefix = squares.map(word => word[step]).join("");
let cdds = getWordsWithPrefix(prefix);
for(const cdd of cdds){
squares.push(cdd);
backtracking(step + 1, squares);
squares.pop();
}
};
for(const word of words){
squares = [word];
backtracking(1, squares);
/* console.log(results); */
}
return results;
};
该代码是LC-425的题解,我在做这道题的时候JS的题解只有一个(哈哈,没办法,只能说明开会员的人不多),而且解法是哈希表+DFS,但我想找一个字典树+DFS的思路。所以我就去瞧了一眼官方题解的python代码,然后根据它的逻辑重构出JS的代码。
好家伙,这一重构不要紧,很快啊,刷的干出来,但就是不给你AC,你气不气。
然后我就找bug,找了一晚上,第二天早上才发现问题所在:
const backtracking = (step, squares) => {
if(step == n){
results.push(squares); //我在这儿打印results,发现和预期结果是一致的,但是最后results却又不是正确结果
/* console.log(results); */
return;
}
let prefix = squares.map(word => word[step]).join("");
let cdds = getWordsWithPrefix(prefix);
for(const cdd of cdds){
squares.push(cdd);
backtracking(step + 1, squares);
squares.pop();
}
};
我对results一个Ctrl + F,奇怪,我代码里就这个backtracking回溯函数在改变results啊,为什么结果变了啊。
。。。反正我过了好几遍逻辑最后才发现是引用传递的问题:
results.push(squares);
就是这一行代码,应该改成:
results.push([...squares]);
改成这样意味着什么,大家都懂。
哈哈,可恶,一个小问题,疏忽了啊。
数组,对象,都是引用,在赋值的时候是引用传递,它的值会变动,因为在内存中的只有一个实际的对象给你改,其他人拿到的都是引用,要各位注意。
如果还不明白,看看下面这个例子:
let a = [1,2,3], b = ["ax", "in", "g"];
a.push(b);
console.log(a, b);
b.pop();
console.log(a, b);
想想两次打印结果是啥。
哈哈,没错,a的值会随b改变。