# \[15]\[简单]\[三指针] 三数之和

## 题目描述

[15. 三数之和](https://leetcode-cn.com/problems/3sum/)

给你一个包含 n 个整数的数组 nums，判断 nums 中是否存在三个元素 a，b，c ，使得 a + b + c = 0 ？请你找出所有满足条件且不重复的三元组。

注意：答案中不可以包含重复的三元组。

示例：

```
给定数组 nums = [-1, 0, 1, 2, -1, -4]，

满足要求的三元组集合为：
[
  [-1, 0, 1],
  [-1, -1, 2]
]
```

## 解题思路

### 退化为两数之和

三数之和, 不能再用哈希表实现了. 一个正常的想法是, 先固定其中一个数值, 问题就退化成了两数之和. 使用[\[1\]\[简单\]\[哈希\] 两数之和](/garnet/suan-fa/shu-zu/1-liang-shu-zhi-he.md)中方法, 需要每固定第一个数, 就要建立一个新的哈希表, 这样做的时间复杂度是$$O(N^2)$$.

### 三指针

在[\[167\]\[简单\]\[双指针\]\[二分\] 两数之和 II - 输入有序数组](/garnet/suan-fa/shuang-zhi-zhen/167-liang-shu-zhi-he-ii-shu-ru-you-xu-shu-zu.md)中, 对于有序的数组, 使用双指针, 从两头向中间移动求解. 因此我们可以首先对原数组进行排序, 然后可以把固定的第一个数值也看作一个指针, 这样就组成了一组三指针. 虽然时间复杂度还是$$O(N^2)$$, 但由于无需创建哈希表, 空间复杂度得到了降低.

需要注意的是题目要求不可以包含重复的三元组, 除了得到结果去重这种耗时费空间的方法外, 在循环时通过特殊的操作就可以避免将重复的三元组添加到结果列表当中. 在移动第一, 第二个指针, 对于重复的数字, 它们在有序数组中肯定是排列在一起的, 我们只考虑这些重复数字中的第一个, 因为两个或三个不同位置的相同数字, 是可能组成最终的结果. 之后, 对于重复数字的第二个(第二个指针从重复数字的第三个起), 直接pass, 因为对应的答案肯定已经在结果列表里了.

```python
class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        ans = []
        nums.sort()

        for first in range(len(nums) - 2):
            if first > 0 and nums[first] == nums[first - 1]:  # 第一个指针, 只考虑重复数字中的第一个
                continue

            third = len(nums) - 1
            target = -nums[first]

            for second in range(first + 1, len(nums) - 1):
                if second > first + 1 and nums[second] == nums[second - 1]:  # 第二个指针, 只考虑第一个指针后, 重复数字中的第一个
                    continue

                second_num = nums[second]
                while third > second and second_num + nums[third] > target:
                    third -= 1
                if third <= second:
                    break
                if second_num + nums[third] == target:
                    ans.append([nums[first], second_num, nums[third]])

        return ans
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://kerasnoone.gitbook.io/garnet/suan-fa/duo-zhi-zhen/15-san-shu-zhi-he.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
