每日算法练习: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. 与两数之和的区别
    • 两数之和:用哈希表或双指针。
    • 三数之和:必须排序 + 双指针,且重点在去重。

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

相关推荐
田梓燊1 天前
力扣:23.合并 K 个升序链表
算法·leetcode·链表
re林檎1 天前
算法札记——4.27
算法
数据牧羊人的成长笔记1 天前
逻辑回归与Softmax回归
算法·回归·逻辑回归
郑州光合科技余经理1 天前
同城O2O海外版二次开发实战:从支付网关到配送算法
开发语言·前端·后端·算法·架构·uni-app·php
d111111111d1 天前
STM32-UART封装问题解析
笔记·stm32·单片机·嵌入式硬件·学习·算法
Jiangxl~1 天前
IP数据云如何为不同行业提供精准IP查询与风险防控解决方案?
网络·网络协议·tcp/ip·算法·ai·ip·安全架构
李伟_Li慢慢1 天前
wolfram详解山峦算法
前端·算法
counting money1 天前
prim算法最小生成树(java)
算法
澈2071 天前
C++面向对象:类与对象核心解析
c++·算法
用户690673881921 天前
基于无人机的单目测距系统,平均误差仅2.12%
算法