题目描述
思路解析动画文字版
记住这条「排序 + 双层固定 + 对撞双指针」,下面每一帧都在套它。
第一步永远是排序:[1,0,-1,0,-2,2,3] 排成 [-2,-1,0,0,1,2,3]。排好序,指针才能靠「和的大小」判断该往哪移、相邻相同值才好去重。
固定前两个数:i 指下标 0(值 -2)、j 指下标 1(值 -1)。剩下要在 j 右边凑出 0 − -2 − -1 = 3。左指针 l 摆在下标 2、右指针 r 摆在下标 6,开始对撞。
正好凑齐!-2+-1+0+3 = 0,记录四元组 [-2,-1,0,3](绿色高亮)。命中后 l 右移、r 左移继续找别的组合。
命中后 l、r 内移,跳过和刚才重复的值(左边),现在 l 到下标 4、r 到下标 5,两边还没相遇,继续对撞。
正好凑齐!-2+-1+1+2 = 0,记录四元组 [-2,-1,1,2](绿色高亮)。命中后 l 右移、r 左移继续找别的组合。
命中后 l、r 内移,现在 l、r 相遇,这一对 i、j 下的内层搜索结束,回去推进 j 或 i。
固定前两个数:i 指下标 0(值 -2)、j 指下标 2(值 0)。剩下要在 j 右边凑出 0 − -2 − 0 = 2。左指针 l 摆在下标 3、右指针 r 摆在下标 6,开始对撞。
四数之和 = 1 > 0,太大了。把右指针左移一格到下标 5(值 2),让和变小。
正好凑齐!-2+0+0+2 = 0,记录四元组 [-2,0,0,2](绿色高亮)。命中后 l 右移、r 左移继续找别的组合。
命中后 l、r 内移,现在 l、r 相遇,这一对 i、j 下的内层搜索结束,回去推进 j 或 i。
固定前两个数:i 指下标 0(值 -2)、j 指下标 4(值 1)。剩下要在 j 右边凑出 0 − -2 − 1 = 1。左指针 l 摆在下标 5、右指针 r 摆在下标 6,开始对撞。
四数之和 = 4 > 0,太大了。把右指针左移一格到下标 5(值 2),让和变小。
固定前两个数:i 指下标 1(值 -1)、j 指下标 2(值 0)。剩下要在 j 右边凑出 0 − -1 − 0 = 1。左指针 l 摆在下标 3、右指针 r 摆在下标 6,开始对撞。
四数之和 = 2 > 0,太大了。把右指针左移一格到下标 5(值 2),让和变小。
四数之和 = 1 > 0,太大了。把右指针左移一格到下标 4(值 1),让和变小。
正好凑齐!-1+0+0+1 = 0,记录四元组 [-1,0,0,1](绿色高亮)。命中后 l 右移、r 左移继续找别的组合。
命中后 l、r 内移,现在 l、r 相遇,这一对 i、j 下的内层搜索结束,回去推进 j 或 i。
固定前两个数:i 指下标 1(值 -1)、j 指下标 4(值 1)。剩下要在 j 右边凑出 0 − -1 − 1 = 0。左指针 l 摆在下标 5、右指针 r 摆在下标 6,开始对撞。
四数之和 = 5 > 0,太大了。把右指针左移一格到下标 5(值 2),让和变小。
固定前两个数:i 指下标 2(值 0)、j 指下标 3(值 0)。剩下要在 j 右边凑出 0 − 0 − 0 = 0。左指针 l 摆在下标 4、右指针 r 摆在下标 6,开始对撞。
四数之和 = 4 > 0,太大了。把右指针左移一格到下标 5(值 2),让和变小。
四数之和 = 3 > 0,太大了。把右指针左移一格到下标 4(值 1),让和变小。
固定前两个数:i 指下标 2(值 0)、j 指下标 4(值 1)。剩下要在 j 右边凑出 0 − 0 − 1 = -1。左指针 l 摆在下标 5、右指针 r 摆在下标 6,开始对撞。
四数之和 = 6 > 0,太大了。把右指针左移一格到下标 5(值 2),让和变小。
整趟扫完,i/j 两层固定 + l/r 对撞,去重后收齐 4 个四元组:[-2,-1,0,3]、[-2,-1,1,2]、[-2,0,0,2]、[-1,0,0,1]。两层固定各约 n 次、最内层对撞线性,总复杂度 O(n³),比暴力四重循环 O(n⁴) 少一个量级。
边界先想清:长度不足返回空、全相同靠去重收敛、大数靠 long。
两个高频追问:K 数之和的扩展规律、long 防溢出的原因。
参考代码
def fourSum(nums, target): nums.sort(); n = len(nums); res = [] for i in range(n - 3): if i > 0 and nums[i] == nums[i-1]: continue # i 去重 for j in range(i + 1, n - 2): if j > i+1 and nums[j] == nums[j-1]: continue # j 去重 l, r = j + 1, n - 1 while l < r: s = nums[i] + nums[j] + nums[l] + nums[r] if s == target: res.append([nums[i], nums[j], nums[l], nums[r]]) l += 1; r -= 1 while l < r and nums[l] == nums[l-1]: l += 1 # l 去重 while l < r and nums[r] == nums[r+1]: r -= 1 # r 去重 elif s < target: l += 1 else: r -= 1 return res复杂度
- 时间:O(n³),i、j 两层各约 n 次,最内层对撞线性
- 空间:O(1),排序原地,只用几个指针(不含答案数组)
易错点
面试追问把动画讲成自己的话
追问K 数之和怎么扩展?
追问为什么累加要用 long?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
删除有序数组中的重复项
LeetCode 26 · 简单 · 沿着 双指针 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题