
本文详解如何在“两数之和”问题中避免生成重复数对(如 (4,8) 和 (8,4))及自匹配(如 (6,6) 仅当同一索引被误用时),通过双指针式遍历与逻辑约束实现天然去重,无需额外哈希去重开销。
本文详解如何在“两数之和”问题中避免生成重复数对(如 (4,8) 和 (8,4))及自匹配(如 (6,6) 仅当同一索引被误用时),通过双指针式遍历与逻辑约束实现天然去重,无需额外哈希去重开销。
在经典的「两数之和」问题中,目标是找出数组中两个不同位置的元素,使其和等于目标值,并返回它们的数值对(非索引)。但许多初学者采用哈希表预存值→遍历查找的策略时,容易因未控制遍历顺序与匹配条件,导致重复结果——例如输入 {1, 4, 3, 5, 6, 8}、target = 12 时输出 (4,8)、(6,6)、(8,4),其中 (4,8) 与 (8,4) 语义重复,而 (6,6) 若源自同一元素(如 arr[4] = 6 被用了两次)则违反“两个数”的题意。
根本原因在于原哈希方案存在两大缺陷:
- 无索引约束:map.put(arr[i], i) 覆盖同值的多个索引,后续 map.get(target - j) 可能取到与当前 j 相同的索引;
- 无序遍历:对 arr 元素直接遍历,未保证 i < j,导致 (a,b) 和 (b,a) 均被加入。
✅ 正确解法是回归本质:使用嵌套循环 + 严格索引偏移,天然规避重复与自匹配:
public static class Pair {
int x, y;
public Pair(int x, int y) { this.x = x; this.y = y; }
@Override
public String toString() {
return "{" + "x=" + x + ", y=" + y + '}';
}
}
public static ArrayList<Pair> twoSum(int[] arr, int target) {
ArrayList<Pair> result = new ArrayList<>();
// 外层遍历至倒数第二个元素,内层从 i+1 开始 → 确保 i < j,杜绝自匹配与顺序颠倒
for (int i = 0; i < arr.length; i++) {
for (int j = i + 1; j < arr.length; j++) {
if (arr[i] + arr[j] == target) {
result.add(new Pair(arr[i], arr[j])); // 存储数值对
}
}
}
return result;
}调用示例:
int[] arr = {1, 4, 3, 5, 6, 8};
int target = 9;
System.out.println(twoSum(arr, target));
// 输出:[{x=1, y=8}, {x=4, y=5}, {x=3, y=6}]? 关键设计说明:
- j = i + 1 保证每对 (i,j) 唯一且 i < j,因此 (arr[i], arr[j]) 与 (arr[j], arr[i]) 不会同时出现;
- 若需返回索引对(如 LeetCode 原题要求),只需将 new Pair(i, j) 替换即可,逻辑不变;
- 时间复杂度为 O(n²),空间复杂度 O(1)(不计结果集),适用于中小规模数据;若需 O(n) 时间,可改用哈希表 + 单次遍历(边查边存),但必须在 put 前检查 containsKey,且只添加 i < map.get(complement) 的组合以保唯一性——不过双循环方案更直观、鲁棒、无去重烦恼。
总结:去重不是“事后过滤”,而是“事前预防”。通过索引约束与遍历顺序设计,让合法解自然唯一,这才是工程实践中最可靠、最易维护的解法。

















