每日算法练习: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 (nums[i]=-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 (nums[i]=-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 (nums[i]=-1):与 i=1 重复,跳过。
  4. i=3 (nums[i]=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. 与两数之和的区别
    • 两数之和:用哈希表或双指针。
    • 三数之和:必须排序 + 双指针,且重点在去重。

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

相关推荐
2301_822703202 小时前
开源鸿蒙跨平台Flutter开发:跨端图形渲染引擎的类型边界与命名空间陷阱:以多维雷达图绘制中的 dart:ui 及 StrokeJoin 异常为例
算法·flutter·ui·开源·图形渲染·harmonyos·鸿蒙
y = xⁿ2 小时前
【LeetCode Hot100】双指针:分离指针
算法·leetcode
学习永无止境@2 小时前
Verilog中有符号数计算
图像处理·算法·fpga开发
6Hzlia2 小时前
【Hot 100 刷题计划】 LeetCode 41. 缺失的第一个正数 | C++ 原地哈希题解
c++·leetcode·哈希算法
小肝一下2 小时前
每日两道力扣,day6
数据结构·c++·算法·leetcode·双指针·hot100
ambition202423 小时前
【算法详解】飞机降落问题:DFS剪枝解决调度问题
c语言·数据结构·c++·算法·深度优先·图搜索算法
徒 花3 小时前
Python知识学习08
java·python·算法
chushiyunen3 小时前
milvus笔记、常用表结构
笔记·算法·milvus
YunQuality3 小时前
六西格玛黑带三个月拿证经验分享
笔记·职场和发展·职场·学习方法