1. 三数之和中排序+双指针的去重细节为何是高频扣分点?
求解三数之和,说明排序+双指针的去重细节,以及为何去重是高频扣分点?
- 排序 + 固定一个数 + 双指针
- 跳过重复元素(外层与内层)防重复结果
- 去重时机与边界
排序后固定第一个数 i,用双指针 l=i+1、r=n-1 找两数和为 -nums[i]。去重细节:① 外层 i 去重:若 nums[i]==nums[i-1] 则跳过(避免同一首元素重复枚举,因为排序后相同值相邻);② 内层去重:当找到一组解后,l 向右跳过与 nums[l] 相同的值,r 向左跳过与 nums[r] 相同的值,避免同一尾对重复。去重是"高频扣分点"是因为:只去重 l/r 而不去重 i,或去重时机在更新前而非更新后,都会产生重复三元组;且 i 去重必须用 i-1 而非 i+1(否则会漏掉可行解)。正确写法:i 去重用 if(i>0 && nums[i]==nums[i-1]) continue;找到解后 while(l<r && nums[l]==nums[l+1]) l++; while(l<r && nums[r]==nums[r-1]) r--; 再 l++, r--。
去重本质是"排序后的相邻跳过",保证每个值组合只枚举一次。i 去重用 i-1 是防止漏解(若用 i+1 会把 i 与 i+1 相同但 i 是必要首元的情况也跳过)。内层去重发生在"找到解之后",否则去重会破坏解。这些细节决定输出是否含重复三元组,故常被面试官深挖。
List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> res = new ArrayList<>();
for (int i = 0; i < nums.length - 2; i++) {
if (i > 0 && nums[i] == nums[i - 1]) continue; // 外层去重
int l = i + 1, r = nums.length - 1, target = -nums[i];
while (l < r) {
int s = nums[l] + nums[r];
if (s == target) {
res.add(Arrays.asList(nums[i], nums[l], nums[r]));
while (l < r && nums[l] == nums[l + 1]) l++; // 内层去重
while (l < r && nums[r] == nums[r - 1]) r--;
l++; r--;
} else if (s < target) l++;
else r--;
}
}
return res;
}