三数之和的简化版为 两数之和 。
方法一:排序 + 双指针夹逼
- 这道题的关键在于去重,而通过对 nums 进行排序,能高效地实现去重的操作,因为经过排序,重复的数字一定是临近的
- 当确定了第一个数字的时候,其实就变成了 two sum 的问题,也就是 leetcode 的第一题。
思路:
- 通过遍历确定第一个数字
- 由于经过排序,数字是从小到大排列的,如果第一个数字都大于0,那么后面的数字无论如何想加,三数之和都不可能等于 0 的,这时候直接退出循环即可。
- 当确定了第一个数字之后,就转换成 two sum 问题,也就是 -a = b + c 的问题求解。我们以 a 的后一位作为 b 的初始位置,而已数组的最后一位作为 c 的初始位置。而求解 -a = b + c 的过程就是 b + c 不断逼近的过程。
- 如果 -a > b + c,由于 b 比 c 小,为了达到 -a = b + c,那么就应该增大 b,也就是将 b 向左移动。
- 如果 -a < b + c,由于 c 比 b 大,为了达到 -a = b + c,那么就应该减少 c,就是将 c 向右移动。
- 如果 -a = b + c,那么此时就已经找到 a + b + c = 0 的组合了。
function threeSum(nums: number[]): number[][] {const ans = []const len = nums.lengthif (len < 3) {return ans}// 做升序处理nums.sort((a, b) => a - b)for (let i = 0; i < len - 2; i++) {// 如果 i 大于 0,三数之和一定大于 0,直接退出 for 循环if (nums[i] > 0) break// 如果 i 与之前相同,则直接进入下一轮循环,避免重复if (i > 0 && nums[i] === nums[i-1]) continuelet l = i + 1,r = len - 1while(l < r) {const sum = nums[i] + nums[l] + nums[r]if (sum === 0) {ans.push([nums[i], nums[l], nums[r]])// l 和 r 继续向中间逼近while(l < r && nums[l] === nums[l + 1]) l++while(l < r && nums[r] === nums[r - 1]) r--l++r--} else if (sum < 0) {l++} else if (sum > 0) {r--}}}return ans};
- 时间复杂度
- 空间复杂度
