Python递归函数中如何正确存储矩阵列表形式的N皇后求解结果
N皇后回溯存储结果异常问题解答
核心原因
Python中列表是引用传递的可变对象,你代码里的问题本质和列表维度没有直接关系,核心是两点:
- 整个回溯流程中,你始终在操作全局唯一初始化的那一个
board对象:所有放皇后、回溯撤销皇后的操作,都是在这个对象上做原地修改,从来没有生成过新的board对象。 - 你写
res.append(board)时,存入res的不是当前棋盘状态的快照副本,而是这个全局board对象的内存地址引用。也就是说res里存的所有元素,本质都指向同一个board列表——后续回溯把Q改回.的时候,res里已经存的“解”自然会跟着一起变,最后所有存进去的内容都会变成回溯完成后的全.空棋盘。
为什么有人存一维列表不用deepcopy?
这是场景差异,不是列表维度的魔法:
- 如果回溯一维列表时用的是原地修改的写法(比如
path.append(val); backtrack(); path.pop()),直接res.append(path)会出现和你完全一样的问题,存进去的结果会跟着回溯被修改,这种场景哪怕是一维列表也必须做拷贝。 - 你看到的不需要拷贝的一维列表场景,基本都是用了生成新对象的写法:比如传参时写
backtrack(path + [val]),这种写法每次递归都会生成一个全新的列表对象,append之后原路径不会再被修改,自然不需要额外拷贝。
为什么二维列表浅拷贝没用?
浅拷贝(比如board.copy()、list(board))只会复制最外层的列表容器,内层嵌套的每一行列表仍然是原对象的引用。回溯时修改内层行里的字符,浅拷贝出来的对象内容还是会同步变化,所以这种嵌套可变对象的场景,要么用copy.deepcopy()做全量拷贝,要么手动生成全新的结果对象。
更高效的修复方案
除了用deepcopy之外,找到合法解时直接生成不可变的字符串结果存入res即可,不需要额外导入copy模块,性能也更好,把你标注的混淆位置的代码替换成下面的内容就行:
# 原错误写法:res.append(board) res.append( [''.join(row) for row in board] )
这段列表推导会遍历棋盘的每一行,把字符列表拼接成不可变的字符串,最终生成一个和原board完全独立的新列表,后续回溯修改原board不会对已经存入res的解产生任何影响。
内容的提问来源于stack exchange,提问作者Bill Zh
相关产品推荐
相关产品推荐

