
本文详解如何修改传统 two sum 解法,使其支持数组中存在重复元素的情况(如 [3,2,3]、target=6),并正确返回所有满足条件的索引对组合,兼顾时间效率与结果完整性。
在标准 Two Sum 问题中,若数组元素唯一且只需返回一对索引(如 LeetCode #1),常用哈希表一次遍历即可实现 O(n) 时间复杂度。但当输入含重复值(如 [3, 2, 3])且目标为 6 时,原逻辑易遗漏 nums[0] + nums[2] === 6 这一有效解——因为简单哈希映射通常只保存某值的最后一个索引,导致早期出现的相同值被覆盖。
你的初始实现 nums.at(index + 1) 仅检查相邻元素,本质是 O(n) 的暴力近似,既无法处理非相邻重复(如 [3,2,3] 中索引 0 和 2),也无法扩展至多解场景,因此不适用于通用 Two Sum。
✅ 推荐方案:哈希表存储值 → 索引数组映射
核心思想:用 Map
该方案时间复杂度仍为 O(n)(单次遍历,Map 操作均摊 O(1)),空间复杂度 O(n),且天然支持任意数量重复元素与全部解的枚举。
const twoSum = (nums, target) => {
return nums.reduce((acc, ele, index) => {
const complement = target - ele;
// 若补数已存在,遍历其所有历史索引,生成索引对
if (acc.map.has(complement)) {
for (const compIndex of acc.map.get(complement)) {
acc.result.push([compIndex, index]);
}
}
// 将当前元素索引存入 map(支持重复值)
if (!acc.map.has(ele)) acc.map.set(ele, []);
acc.map.get(ele).push(index);
return acc;
}, { map: new Map(), result: [] }).result;
};
// 测试用例
console.log(twoSum([2, 7, 11, 15], 9)); // [[0, 1]]
console.log(twoSum([3, 2, 3], 6)); // [[0, 2]]
console.log(twoSum([2, 7, 11, 7, 15, 2, 7], 9));
// [[0,1], [0,3], [0,6], [1,5], [3,5], [5,6]]⚠️ 注意事项:
- 返回格式为二维数组 [[i, j]],明确区分索引对顺序(前小后大,按遍历顺序自然保证 i
- 若只需任一解(如 LeetCode 标准题),可在找到首个匹配时提前 return,进一步优化常数性能;
- 避免使用 indexOf 或 lastIndexOf 查找重复值——它们内部是 O(n) 线性扫描,会使整体退化为 O(n²);
- 此解法不修改原数组,符合函数式编程原则,也便于测试与复用。
总结:处理非唯一元素的关键,在于放弃“值→单索引”的映射思维,升级为“值→索引集合”。这一转变既保持了线性时间效率,又完整覆盖了所有合法索引组合,是工业级代码中应对数据不确定性的典型范式。










