Skip to content

15. 三数之和

题目

给你一个整数数组 nums,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k,同时还满足 nums[i] + nums[j] + nums[k] == 0

请你返回所有和为 0不重复 的三元组。

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

示例 1:

plain
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2]。
注意,输出的顺序和三元组的顺序并不重要。

示例 2:

plain
输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。

示例 3:

plain
输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。

提示:

  • 3 <= nums.length <= 3000
  • -10^5 <= nums[i] <= 10^5

解答

思路

数组是无序的,要使用相向双指针,数组必须要是 有序的

三元组的顺序不重要,因此我们可以给 i, j, k 规定一个顺序,方便我们枚举。

我们可以枚举 nums[i],将问题转化为 nums[j] + nums[k] == -nums[i] 的问题。

题目还要求答案中不包含重复的三元组,例如:

plain
-4 -1 -1 0 1 2

nums[1] + nums[3] + nums[4] = 0, (-1, 0, 1)
nums[2] + nums[3] + nums[4] = 0, (-1, 0, 1)
这就是重复的三元组,需要舍去

因此:只要当前枚举的 nums[i] 和上一个枚举的 nums[i - 1] 是相同的,我们就跳过它即可。

复杂度:

  • 时间复杂度为 O(n2)
  • 空间复杂度为 O(1)

代码

C++ 代码

cpp
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        vector<vector<int>> res;

        for (int i = 0; i < nums.size() - 2; i ++) {
            if (i > 0 && nums[i - 1] == nums[i]) {
                continue;
            }

            int j = i + 1;
            int k = nums.size() - 1;

            while (j < k) {
                int sum = nums[i] + nums[j] + nums[k];

                if (sum == 0) {
                    res.push_back({nums[i], nums[j], nums[k]});
                    
                    j ++;
                    while (j < k && nums[j] == nums[j - 1]) {
                        j ++;
                    }
                    
                    k --;
                    while (j < k && nums[k] == nums[k + 1]) {
                        k --;
                    }
                }
                else if (sum < 0) {
                    j ++;
                }
                else {
                    k --;
                }
            }
        }    

        return res;    
    }
};

Python 代码

python
class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        # i < j < k
        ans = []
        n = len(nums)

        for i in range(n - 2):
            x = nums[i]
            if i > 0 and x == nums[i - 1]:
                continue
            
            j = i + 1
            k = n - 1

            while (j < k):
                s = x + nums[j] + nums[k]

                if s > 0:
                    k -= 1
                elif s < 0:
                    j += 1
                else:
                    ans.append([x, nums[j], nums[k]])

                    j += 1
                    while j < k and nums[j] == nums[j - 1]:
                        j += 1
                    
                    k -= 1
                    while j < k and nums[k] == nums[k + 1]:
                        k -= 1
        return ans

一些小优化:

对于每个 i,首先计算 nums[i] + nums[i + 1] + nums[i + 2] 是否大于零。如果 > 0,说明 i 以及之后的所有数都不可能成为答案,因此直接 break

然后再计算 nums[i] + nums[n - 2] + nums[n - 1] 是否小于零,如果 < 0,说明当前 i 范围内的所有数都不可能成为答案,因此直接 continue

python
class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        # i < j < k
        ans = []
        n = len(nums)

        for i in range(n - 2):
            x = nums[i]
            if i > 0 and x == nums[i - 1]:
                continue

            if x + nums[i + 1] + nums[i + 2] > 0:
                break
            
            if x + nums[-2] + nums[-1] < 0:
                continue
            
            j = i + 1
            k = n - 1

            while (j < k):
                s = x + nums[j] + nums[k]

                if s > 0:
                    k -= 1
                elif s < 0:
                    j += 1
                else:
                    ans.append([x, nums[j], nums[k]])

                    j += 1
                    while j < k and nums[j] == nums[j - 1]:
                        j += 1
                    
                    k -= 1
                    while j < k and nums[k] == nums[k + 1]:
                        k -= 1
        return ans

结果:

plain
time:  480 ms,   96.26%
space: 20.03 MB, 20.28%

Released under the MIT License.