每日算法练习:LeetCode 15. 三数之和 ✅

大家好,我是你们的算法小伙伴。今天我们来练习一道经典的中等难度题 ------LeetCode 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 = 0nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0nums[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]]

提示:

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

解题思路

核心思路转化

暴力枚举三元组的时间复杂度为 O(n^3),对于 n=3000 的规模完全无法通过。优化方向 :将「三数之和」转化为「两数之和」,利用排序 + 双指针将复杂度优化到 O(n^2)。

解题步骤

  1. 排序:先对数组排序,为双指针和去重打下基础。
  2. 固定一个数 :遍历数组,将每个元素 nums[i] 作为第一个数,目标转化为在剩余元素中找两数之和等于 -nums[i]
  3. 双指针找两数之和 :在 i+1n-1 区间内,用左右指针寻找满足条件的组合。
  4. 关键去重 :这是本题的难点,必须对 nums[i]leftright 对应的元素进行精准去重。

代码实现

排序 + 双指针法

java 复制代码
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        int n = nums.length;
        
        // 1. 排序(关键步骤)
        Arrays.sort(nums);
        
        // 2. 遍历固定第一个数
        for (int i = 0; i < n; i++) {
            // 去重:如果当前数和前一个数相同,跳过(避免重复三元组)
            if (i > 0 && nums[i] == nums[i-1]) {
                continue;
            }
            
            // 优化:如果第一个数已经大于0,后续不可能和为0,直接返回
            if (nums[i] > 0) {
                return result;
            }
            
            int target = -nums[i];
            int left = i + 1;
            int right = n - 1;
            
            // 3. 双指针找两数之和
            while (left < right) {
                int sum = nums[left] + nums[right];
                if (sum == target) {
                    // 找到一组解,加入结果集
                    result.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    
                    // 去重:移动指针时,跳过相同的元素
                    while (left < right && nums[left] == nums[left+1]) {
                        left++;
                    }
                    while (left < right && nums[right] == nums[right-1]) {
                        right--;
                    }
                    
                    // 找到解后,双指针同时移动
                    left++;
                    right--;
                } else if (sum < target) {
                    // 和太小,左指针右移
                    left++;
                } else {
                    // 和太大,右指针左移
                    right--;
                }
            }
        }
        return result;
    }
}

代码详解

一、排序

Arrays.sort(nums) 是核心前提,不仅让双指针移动有方向,还为去重提供了依据(重复元素相邻)。

二、固定第一个数与去重

  1. 去重逻辑if (i > 0 && nums[i] == nums[i-1]) continue
    • 如果当前数和前一个数相同,说明前一个数已经处理过该组合,跳过以避免重复。
  2. 剪枝优化if (nums[i] > 0) return result
    • 数组有序,若第一个数为正,后续数均为正,三者之和不可能为 0,直接终止循环。

三、双指针找两数之和

  1. 指针初始化left = i + 1(避开固定数),right = n - 1(数组末尾)。
  2. 指针移动
    • sum == target:找到解,记录并去重,然后同时移动指针。
    • sum < target:需要增大和,左指针右移。
    • sum > target:需要减小和,右指针左移。
  3. 内部去重 :找到解后,必须跳过 leftright 指向的重复元素,防止结果集中出现重复三元组。

示例 1 模拟

输入:nums = [-1,0,1,2,-1,-4]排序后:[-4, -1, -1, 0, 1, 2]

  1. i=0 (numsi=-4) ,target=4:
    • left=1 (-1), right=5 (2) → sum=1 < 4 → left++
    • left=2 (-1), right=5 (2) → sum=1 < 4 → left++
    • left=3 (0), right=5 (2) → sum=2 < 4 → left++
    • left=4 (1), right=5 (2) → sum=3 < 4 → left++ (left >= right,结束)
  2. i=1 (numsi=-1) ,target=1:
    • left=2 (-1), right=5 (2) → sum=1 == 1 → 记录 [-1,-1,2]
    • 去重:left 移至 3 (0),right 移至 4 (1)
    • left=3 (0), right=4 (1) → sum=1 == 1 → 记录 [-1,0,1]
  3. i=2 (numsi=-1):与 i=1 重复,跳过。
  4. i=3 (numsi=0) :后续和均 >= 0,无结果。最终结果:[[-1,-1,2], [-1,0,1]]

复杂度分析

解法 时间复杂度 空间复杂度 说明
排序 + 双指针 O(n^2) O(logn) (排序栈空间) 排序 O(nlogn),遍历 O(n^2),主导项为 O(n^2)

总结

  1. 核心考点 :本题是排序 + 双指针的经典组合,是面试中 O(n2) 复杂度算法的典型代表。
  2. 去重是关键 :这道题 90% 的错误都出在去重逻辑上。必须牢记对 ileftright 三处的重复元素都要进行跳过。
  3. 剪枝优化:固定数大于 0 时直接返回,是非常实用的优化,能减少不必要的计算。
  4. 与两数之和的区别
    • 两数之和:用哈希表或双指针。
    • 三数之和:必须排序 + 双指针,且重点在去重。

今天的每日算法练习就到这里,我们明天再见!👋

相关推荐
可编程芯片开发3 分钟前
基于FOC控制器的BLDC无刷直流电机控制系统matlab编程与仿真
算法
aaaameliaaa31 分钟前
进制练习题【找出只出现一次的数字、交换两个变量(不创建临时变量)、统计二进制中1的个数、打印整数二进制的奇数位和偶数位、求两个数二进制中不同位的个数】
c语言·数据结构·笔记·算法
QiLinkOS2 小时前
第三视觉理解徐玉生与他的商业活动(28)
大数据·c++·人工智能·算法·开源协议
码云数智-大飞3 小时前
从 OC 平滑迁移 Swift 完整方案
职场和发展·蓝桥杯
wabs6663 小时前
关于动态规划【力扣1143.最长公共子序列的思考】
算法·leetcode·动态规划
剑挑星河月3 小时前
54.螺旋矩阵
java·算法·leetcode·矩阵
Robot_Nav3 小时前
MPPI 局部规划器实验设计讲解
人工智能·算法·mppi
mingo_敏4 小时前
Mean-Teacher 均值教师自训练框架详解
算法·均值算法
水木流年追梦4 小时前
agent面试必备31- AI Agent 核心进阶:工具路由(Tool Routing)
数据库·人工智能·oracle·面试·职场和发展·embedding
星空露珠4 小时前
迷你世界UGc3.0脚本Wiki[剧情动画模块管理接口 Timeline]
开发语言·数据结构·算法·游戏·lua