
本文详解 JavaScript 递归生成子集时因数组引用传递导致 all_sets 存入空数组的问题,指出根本原因是多次 push 同一 temp 引用,最终所有元素指向已清空的数组;提供深拷贝修复方案,并给出完整可运行代码与关键注意事项。
本文详解 javascript 递归生成子集时因数组引用传递导致 `all_sets` 存入空数组的问题,指出根本原因是多次 `push` 同一 `temp` 引用,最终所有元素指向已清空的数组;提供深拷贝修复方案,并给出完整可运行代码与关键注意事项。
在 JavaScript 中实现子集(Power Set)生成时,一个常见且隐蔽的陷阱是:误将可变数组的引用而非副本存入结果集合。上述代码看似逻辑正确——通过回溯(push/pop)构建每个子集,并在递归终止时将其加入 all_sets,但最终输出却全是空数组 []。其根源在于 JavaScript 中对象(包括数组)按引用传递:temp 始终是同一个数组实例,而 all_sets.push(temp) 实际存入的是该实例的引用。随着递归回退,temp.pop() 持续修改这个共享数组,当整个调用栈展开完毕后,all_sets 中所有引用都指向一个已被清空的 temp。
解决方法非常简洁:在终止条件处存入 temp 的浅拷贝,而非引用本身。由于 temp 只包含基本类型(数字),使用扩展运算符 [...temp] 即可安全创建独立副本:
function generate(nums, curr_index, temp, all_sets) {
if (curr_index === nums.length) {
all_sets.push([...temp]); // ✅ 创建新数组副本,避免引用污染
return;
}
temp.push(nums[curr_index]);
generate(nums, curr_index + 1, temp, all_sets);
temp.pop(); // 回溯:撤销当前选择
generate(nums, curr_index + 1, temp, all_sets);
}
function generateAllSubsets(nums) {
const all_sets = [];
generate(nums, 0, [], all_sets);
return all_sets;
}
console.log(generateAllSubsets([1, 2, 3]));
// 输出: [[], [1], [1,2], [1,2,3], [1,3], [2], [2,3], [3]]⚠️ 关键注意事项:
- 不要使用
all_sets.push(temp.slice())或all_sets.push(Array.from(temp))等等效写法——它们同样有效,但[...temp]更简洁直观;- 若
nums包含嵌套对象,需改用深拷贝(如JSON.parse(JSON.stringify(temp))或结构化克隆),但本题场景下浅拷贝已完全足够;- 此问题本质是“可变状态共享”反模式,在递归+回溯类算法中高频出现,务必养成对引用类型结果存档时主动拷贝的习惯。
通过这一微小但关键的修改,代码即可正确生成全部 $2^n$ 个子集,兼顾时间效率与逻辑清晰性。
立即学习“Java免费学习笔记(深入)”;


















