如何高效解决含重复元素的 Two Sum 问题(返回所有索引对)

9次阅读

如何高效解决含重复元素的 Two Sum 问题(返回所有索引对)

本文详解如何修改传统 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 记录每个数值出现的所有索引。遍历过程中,对当前元素 ele 计算补数 complement = target – ele;若该补数已存在于 Map 中,则将 所有已记录的补数索引 与当前索引 index 组合成 [compIndex, index],加入结果集。最后将当前 ele 的索引追加到其对应索引数组中(即使已存在,也要保留全部位置)。

该方案时间复杂度仍为 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²);
  • 此解法不修改原数组,符合函数式编程原则,也便于测试与复用。

总结:处理非唯一元素的关键,在于放弃“值→单索引”的映射思维,升级为“值→索引集合”。这一转变既保持了线性时间效率,又完整覆盖了所有合法索引组合,是工业级代码中应对数据不确定性的典型范式。

text=ZqhQzanResources