Leecode#15刷题之三数之和

题目

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

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

示例 1:

复制代码
输入: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:

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

示例 3:

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

提示:

  • 3 <= nums.length <= 3000
  • -105 <= nums[i] <= 105

Java语言

java 复制代码
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(nums);

        for (int i = 0; i < nums.length - 2; i++) {
            // 跳过重复的第一个数
            if (i > 0 && nums[i] == nums[i - 1]) continue;

            // 剪枝:最小的三个数都 > 0,不可能和为 0
            if (nums[i] + nums[i + 1] + nums[i + 2] > 0) break;
            // 剪枝:当前数 + 最大的两个数都 < 0,跳过当前数
            if (nums[i] + nums[nums.length - 2] + nums[nums.length - 1] < 0) continue;

            int left = i + 1, right = nums.length - 1;
            while (left < right) {
                int sum = nums[i] + nums[left] + nums[right];

                if (sum < 0) {
                    left++;
                } else if (sum > 0) {
                    right--;
                } else {
                    result.add(Arrays.asList(nums[i], nums[left], nums[right]));

                    // 跳过重复的 left
                    while (left < right && nums[left] == nums[left + 1]) left++;
                    // 跳过重复的 right
                    while (left < right && nums[right] == nums[right - 1]) right--;

                    left++;
                    right--;
                }
            }
        }

        return result;
    }
}

核心思路: 排序 + 双指针。先排序,固定一个数 nums[i],然后用双指针在剩余部分找两数之和等于 -nums[i]


逐行说明:

java 复制代码
List<List<Integer>> result = new ArrayList<>();

Arrays.sort(nums);

创建结果列表。先排序是双指针的前提,排序后重复值相邻,也方便去重和剪枝。

java 复制代码
for (int i = 0; i < nums.length - 2; i++) {

遍历第一个数,到 length - 2 停止(至少留两个位置给双指针)。

java 复制代码
if (i > 0 && nums[i] == nums[i - 1]) continue;

去重 :如果当前数和前一个相同,跳过,避免产生重复三元组。例如 [-1,-1,0,1],第一个 -1 找了 [-1,0,1],第二个 -1 跳过。

java 复制代码
if (nums[i] + nums[i + 1] + nums[i + 2] > 0) break;

if (nums[i] + nums[nums.length - 2] + nums[nums.length - 1] < 0) continue;

剪枝优化

  • 最小的三个数之和都 > 0,后面不可能有更小的了,直接 break
  • 当前数加上最大的两个数都 < 0,当前数太小,continue 换下一个
java 复制代码
int left = i + 1, right = nums.length - 1;

while (left < right) {

int sum = nums[i] + nums[left] + nums[right];

双指针,lefti+1 开始,right 从末尾开始。计算三数之和。

java 复制代码
if (sum < 0) {

left++;

} else if (sum > 0) {

right--;

} else {

result.add(Arrays.asList(nums[i], nums[left], nums[right]));
  • 和 < 0:太小了,left 右移让和变大
  • 和 > 0:太大了,right 左移让和变小
  • 和 == 0:找到一组,加入结果
java 复制代码
while (left < right && nums[left] == nums[left + 1]) left++;

while (left < right && nums[right] == nums[right - 1]) right--;

left++;

right--;

去重 :跳过与当前 left/right 相同的值,然后各移动一步继续找下一组。


示例 1 演示: nums = [-1,0,1,2,-1,-4] → 排序后 [-4,-1,-1,0,1,2]

i numsi left right sum 操作 result
0 -4 1 5 -4+(-1)+2=-3 <0, left++ \[\]
-4 2 5 -4+(-1)+2=-3 <0, left++ \[\]
-4 3 5 -4+0+2=-2 <0, left++ \[\]
-4 4 5 -4+1+2=-1 <0, left++ → left>=right 退出 \[\]
1 -1 2 5 -1+(-1)+2=0 找到 \[-1,-1,2]
-1 3 4 -1+0+1=0 找到 \[-1,-1,2,-1,0,1]
2 -1 (与i=1相同,skip) continue
3 0 4 5 0+1+2=3 >0, right-- → 退出

最终返回 [[-1,-1,2],[-1,0,1]]

时间复杂度 O(n²),空间复杂度 O(1)(不计排序)。

python语言

python 复制代码
class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        result = []
        nums.sort()

        for i in range(len(nums) - 2):
            # 跳过重复的第一个数
            if i > 0 and nums[i] == nums[i - 1]:
                continue

            # 剪枝:最小的三个数都 > 0,不可能和为 0
            if nums[i] + nums[i + 1] + nums[i + 2] > 0:
                break
            # 剪枝:当前数 + 最大的两个数都 < 0,跳过当前数
            if nums[i] + nums[-2] + nums[-1] < 0:
                continue

            left, right = i + 1, len(nums) - 1
            while left < right:
                total = nums[i] + nums[left] + nums[right]

                if total < 0:
                    left += 1
                elif total > 0:
                    right -= 1
                else:
                    result.append([nums[i], nums[left], nums[right]])

                    # 跳过重复的 left
                    while left < right and nums[left] == nums[left + 1]:
                        left += 1
                    # 跳过重复的 right
                    while left < right and nums[right] == nums[right - 1]:
                        right -= 1

                    left += 1
                    right -= 1

        return result

逻辑与 Java 版完全一致,逐行说明:

python 复制代码
result = []

nums.sort()

创建结果列表,nums.sort() 原地排序。对应 Java 的 Arrays.sort(nums)

python 复制代码
for i in range(len(nums) - 2):

if i > 0 and nums[i] == nums[i - 1]:

continue

遍历第一个数。nums[i] == nums[i - 1] 跳过重复值,对应 Java 的 nums[i] == nums[i - 1]

python 复制代码
if nums[i] + nums[i + 1] + nums[i + 2] > 0:

break

if nums[i] + nums[-2] + nums[-1] < 0:

continue

剪枝优化。nums[-2]nums[-1] 是 Python 的负索引,直接取倒数第二和最后一个元素,比 Java 的 nums[nums.length - 2] 简洁很多。

python 复制代码
left, right = i + 1, len(nums) - 1

while left < right:

total = nums[i] + nums[left] + nums[right]

双指针。left, right = i + 1, len(nums) - 1 一行同时赋值两个变量。

python 复制代码
if total < 0:

left += 1

elif total > 0:

right -= 1

else:

result.append([nums[i], nums[left], nums[right]])
  • 和 < 0:left 右移
  • 和 > 0:right 左移
  • 和 == 0:result.append([a, b, c]) 添加列表,对应 Java 的 Arrays.asList(a, b, c)
python 复制代码
while left < right and nums[left] == nums[left + 1]:

left += 1

while left < right and nums[right] == nums[right - 1]:

right -= 1

left += 1

right -= 1

跳过重复值后,各移动一步继续找。+= 1 / -= 1 对应 Java 的 ++ / --


与 Java 版的关键区别:

特性 Java Python
排序 Arrays.sort(nums) nums.sort()
列表长度 nums.length len(nums)
添加元素 result.add(Arrays.asList(...)) result.append([...])
负索引 nums[nums.length - 1] nums[-1]
双变量赋值 int left = i+1, right = n-1; left, right = i+1, n-1
自增/自减 left++ / right-- left += 1 / right -= 1

时间复杂度 O(n²),空间复杂度 O(1)(不计排序)。

相关推荐
圣保罗的大教堂29 分钟前
leetcode 877. 石子游戏 中等
leetcode
哭泣方源炼蛊35 分钟前
并查集进阶 P1(带权并查集,并查集分类)
数据结构·c++·算法·二进制·带权并查集
牧羊人.3331 小时前
动手学深度学习 02 | 手写数字识别
图像处理·人工智能·深度学习·算法
武帝为此1 小时前
【InnoDB存储引擎介绍】
数据库·算法
shehuiyuelaiyuehao2 小时前
算法32,连续数组,前缀和+哈希表
算法·leetcode·职场和发展
程序员贺加贝2 小时前
报表大 IN 优化:一条 product_profile 超大 IN SQL 背后的报表任务治理
算法·性能优化
知无不研2 小时前
std::function在使用时遇到的问题
c++·算法·st·function
薛定e的猫咪3 小时前
(IEEE Transactions 2025)自适应元强化学习动态柔性作业车间调度框架
网络·人工智能·算法
YonyouHRSaaS3 小时前
AI面试工具怎么选型?2026年企业AI面试系统选型标准是什么?
人工智能·面试·职场和发展·ai面试·ai招聘