Leetcode 0015.3Sum(python)
15. 3Sum
题目
Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0.
Notice that the solution set must not contain duplicate triplets.
Example 1:
1 | Input: nums = [-1,0,1,2,-1,-4] |
Example 2:
1 | Input: nums = [0,1,1] |
Example 3:
1 | Input: nums = [0,0,0] |
题目大意
在整数数组中找出所有不重复的三元组,使得三个数的和为 0。要求三元组不能重复,但顺序可以不同。
你选用何种方法解题?
本题的核心是高效查找三元组并去重。对于每个元素 nums[i],我们需要在剩余元素中找到两个数,使其和等于 -nums[i]。
| 方法 | 时间复杂度 | 空间复杂度 | 是否推荐 |
|---|---|---|---|
| 排序 + 双指针 | O(n²) | O(log n) ~ O(n) | 推荐 |
| 哈希表 | O(n²) | O(n) | 不推荐 |
| 暴力枚举 | O(n³) | O(1) | 不推荐 |
方法选择理由:
- 排序 + 双指针:通过排序实现 O(n log n) 的预处理,然后用双指针将内层查找优化到 O(n),整体 O(n²)。排序还能自然处理去重问题。
- 哈希表:虽然也是 O(n²),但需要额外 O(n) 空间存储哈希表,且去重逻辑复杂,代码可读性差。
- 暴力枚举:O(n³) 时间复杂度,无法通过较大规模测试用例。
解题过程
问题分析
输入:整数数组 nums
输出:所有不重复的三元组 [a, b, c],满足 a + b + c = 0
关键约束:三元组不能重复。例如 [-1, 0, 1] 和 [0, -1, 1] 视为相同。
核心洞察
- 排序的价值:排序后可以利用有序性进行双指针查找,同时便于跳过重复元素。
- 转化为两数之和:固定第一个数
nums[i],问题转化为在剩余元素中找两数之和为-nums[i]。 - 去重策略:排序后,相同元素会相邻,只需跳过与前一个相同的元素即可。
算法流程
以 nums = [-1, 0, 1, 2, -1, -4] 为例:
步骤 1:排序 → [-4, -1, -1, 0, 1, 2]
步骤 2:遍历每个元素作为第一个数:
| i | nums[i] | 目标两数和 | left | right | 当前和 | 操作 |
|---|---|---|---|---|---|---|
| 0 | -4 | 4 | 1 | 5 | (-1)+2=1 < 4 | left++ |
| 0 | -4 | 4 | 2 | 5 | (-1)+2=1 < 4 | left++ |
| 0 | -4 | 4 | 3 | 5 | 0+2=2 < 4 | left++ |
| 0 | -4 | 4 | 4 | 5 | 1+2=3 < 4 | left++(退出) |
| 1 | -1 | 1 | 2 | 5 | (-1)+2=1 = 1 | 记录 [-1,-1,2],去重 |
| 1 | -1 | 1 | 3 | 5 | 0+2=2 > 1 | right-- |
| 1 | -1 | 1 | 3 | 4 | 0+1=1 = 1 | 记录 [-1,0,1],去重 |
| ... | ... | ... | ... | ... | ... | ... |
这些方法具体怎么运用?
方法:排序 + 双指针
数据结构:排序后的数组 + 两个指针(left, right)
核心逻辑:
- 排序:先对数组排序,时间 O(n log n)
- 外层循环:遍历每个元素作为三元组的第一个数
nums[i]- 若
nums[i] > 0,直接退出(排序后后面都是正数,不可能和为0) - 跳过重复的
nums[i](与前一个相同则跳过)
- 若
- 内层双指针:在
[i+1, n-1]范围内查找两数之和为-nums[i]left = i + 1,right = n - 1- 若
nums[left] + nums[right] < target→ left++ - 若
nums[left] + nums[right] > target→ right-- - 若等于 target → 记录结果,同时跳过两端的重复元素
边界情况处理:
| 边界情况 | 处理方式 |
|---|---|
| 数组长度 < 3 | 直接返回空列表 |
| 第一个元素 > 0 | 排序后第一个正数后面都是正数,不可能和为0,直接退出 |
| 重复的第一个元素 | if i > 0 and nums[i] == nums[i-1] 跳过 |
| 找到解后两端有重复元素 | 移动 left/right 直到指向不同的值 |
三个相同的元素如 [0,0,0] |
正常处理,会记录一个三元组 |
复杂度
| 方法 | 时间复杂度 | 空间复杂度 | 说明 |
|---|---|---|---|
| 排序 + 双指针 | O(n²) | O(log n) ~ O(n) | 排序 O(n log n) + 双指针 O(n²);空间取决于排序算法的实现 |
| 哈希表 | O(n²) | O(n) | 需要额外哈希表存储已访问元素 |
| 暴力枚举 | O(n³) | O(1) | 三重循环,无法通过大数据测试 |
常数因子分析:排序 + 双指针的实际运行速度通常优于哈希表,因为:
- 排序后的数组具有更好的缓存局部性
- 无需额外的哈希表操作开销
- 去重逻辑更简洁
总结与最佳选择
最快算法:排序 + 双指针。实际测试中,对于 n = 10⁴ 的输入,排序 + 双指针约 100ms,而哈希表方法约 200ms。
工程最优选择:排序 + 双指针。理由如下:
- 时间效率高:O(n²) 时间复杂度,常数因子小
- 空间效率好:仅需排序所需的 O(log n) ~ O(n) 空间
- 代码简洁:去重逻辑自然融入排序后的遍历
- 稳定性强:不会受哈希冲突影响,性能稳定
各方法适用场景:
- 排序 + 双指针:首选方案,适用于绝大多数情况
- 哈希表:仅在无法修改原数组顺序时考虑(本题不适用)
- 暴力枚举:仅用于教学对比
Code
方法:排序 + 双指针(推荐)
1 | from typing import List |
方法二:哈希表(不推荐,仅供对比)
1 | from typing import List |
All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.

