题目描述
思路解析动画文字版
排序后固定 i,问题就变成在 i 右边找两个数,让它们的和等于 −nums[i]。l 和 r 往中间夹:和大了 r 左移,和小了 l 右移,正好命中就记下来。
第一步:排序:先从小到大排个序。有序了才能用对撞双指针。
固定 i=0 (−2):固定 −2,要在右边凑出和为 2 的两个数。当前三数和 = −1 小于 0,想让和变大,就把 l 右移(换个更大的数)。
i=0 · l 右移:l 移到下标 2(值 0),三数和 = 1 大于 0 了。这次反过来,把 r 左移(换个更小的数)。
i=0 · r 左移:r 移到下标 3(值 2),三数和 = −2 + 0 + 2 = 0,命中!记录 [−2, 0, 2]。
i=0 · 命中后双移:命中后 l 右移、r 左移 同时进行(并跳过重复值),继续找。此时 l、r 交叉,固定第一个 −2 这轮结束。
i=1 · 又是 −2 · 跳过:轮到固定下标 1,可它也是 −2,和上一个固定值一样。再做一遍只会得到重复的 [−2,0,2],所以直接跳过——这就是「去重」。
固定 i=2 (0):固定 0:最小的两数之和都已经大于 0,r 左移后很快交叉,没有新解。
结束:所有固定点试完,得到唯一三元组 [−2, 0, 2]。这一趟把小了右移、大了左移、命中、去重四种情况都演示了一遍。
排序后用左右指针把两数之和「夹」出来——盛水容器、两数之和 II 都是这套对撞手感。
迁移阶梯:把这题练到能复述后,再去同类题里迁移:先复用状态定义,再调整边界条件。
边界三连:边界三连:极端输入先过一遍。
几个高频追问,记住答法。
参考代码
class Solution: def threeSum(self, nums): nums.sort() ans = [] n = len(nums) for i in range(n - 2): if nums[i] > 0: break if i > 0 and nums[i] == nums[i - 1]: continue l, r = i + 1, n - 1 while l < r: s = nums[i] + nums[l] + nums[r] if s == 0: ans.append([nums[i], nums[l], nums[r]]) l += 1 r -= 1 while l < r and nums[l] == nums[l - 1]: l += 1 while l < r and nums[r] == nums[r + 1]: r -= 1 elif s < 0: l += 1 else: r -= 1 return ans复杂度
- 时间复杂度:O(n²),排序 + 双层 n×n
- 空间复杂度:O(1),不算排序与答案
可套用的代码模板
记住:和小了挪左、和大了挪右。这是三语言通用的对撞骨架——三数之和就是在它外面套一层「固定 i + 跳重复」。
# 有序数组里找一对,逼近某个 targetl, r = 0, len(a) - 1while l < r: s = a[l] + a[r] if s == target: record(); l += 1; r -= 1 # 命中双移 elif s < target: l += 1 # 要更大 else: r -= 1 # 要更小易错点
面试追问把动画讲成自己的话
追问为什么是 O(n²) 不是 O(n³)?
追问重复怎么去重?
追问能用哈希吗?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
盛最多水的容器
LeetCode 11 · 中等 · 沿着 双指针 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题