力扣HOT100-6 三数之和(Java实现)
题目

题目解读
1.如果我们先固定一个数 a,问题就退化为在剩下的数中找 b + c = -a,这恰好是两数之和问题。
2.由于题目要求去重,且数组元素可能重复,使用排序 + 双指针会比哈希表更方便去重。
开始解题
一、排序法
思路:
排序:将数组从小到大排序,这样重复元素会相邻,便于跳过。同时可以利用有序性指导双指针移动。
固定第一个数:遍历数组,将 nums[i] 作为 a。
如果 a > 0,由于数组已排序,后面的数都大于等于 a,三数之和不可能为 0,可以直接结束循环(剪枝优化)。
双指针找另外两个数:在 i+1 到末尾的区间内,用 left 和 right 两个指针分别指向两端:
计算 sum = nums[left] + nums[right]。
如果 sum == target(target = -a),则找到一组解,加入结果。
如果 sum < target,说明需要更大的和,left++。
如果 sum > target,说明需要更小的和,right--。
去重是关键:
外层去重:如果当前 a 和上一个 a 相同,跳过,否则会产生完全相同的三元组。
内层去重:找到一组解后,left 和 right 可能直接指向重复值,如果不跳过,会添加相同的三元组。所以需要将 left 一直右移到值变化,right 一直左移到值变化,然后再同时收缩一位。
核心流程:
1.对数组进行排序 Arrays.sort(nums)。
2.初始化结果列表 List<List<Integer>> result。
3.遍历 i 从 0 到 n-3:
如果 nums[i] > 0,直接 break(剪枝)。
如果 i > 0 且 nums[i] == nums[i-1],continue(跳过重复 a)。
设置 left = i + 1,right = n - 1,target = -nums[i]。
当 left < right 时循环:
计算 sum = nums[left] + nums[right]。
如果 sum == target:
将 [nums[i], nums[left], nums[right]] 加入结果。
执行内层去重:
用 while 循环将 left 移到下一个不同值的位置,将 right 移到上一个不同值的位置。
然后 left++, right-- 继续寻找。
如果 sum < target:left++。
如果 sum > target:right--。
4.返回结果列表。
代码
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);
int n = nums.length;
for (int i = 0; i < n - 2; i++) {
// 剪枝:最小的数已经大于 0,三数之和不可能为 0
if (nums[i] > 0) break;
// 跳过重复的 a
if (i > 0 && nums[i] == nums[i - 1]) continue;
int left = i + 1, right = n - 1;
int target = -nums[i];
while (left < right) {
int sum = nums[left] + nums[right];
if (sum == target) {
result.add(Arrays.asList(nums[i], nums[left], nums[right]));
// 跳过重复的 b
while (left < right && nums[left] == nums[left + 1]) left++;
// 跳过重复的 c
while (left < right && nums[right] == nums[right - 1]) right--;
// 继续收缩
left++;
right--;
} else if (sum < target) {
left++;
} else {
right--;
}
}
}
return result;
}
感谢您能够看到这里,一起见证小何同学的算法学习,如果您有不同的见解,希望能得到您的指点和点悟;如果您是和我一样的同学,也希望这篇文章能对您有所帮助。
更多推荐
所有评论(0)