当前位置: 首页 > news >正文

【Hot100 | 6 LeetCode 15. 三数之和】

这段代码是解决 LeetCode 15. 三数之和 问题的经典排序 + 双指针解法,核心目标是在整数数组中找到所有不重复的三元组(a, b, c),使得 a + b + c = 0。该解法通过排序简化查找逻辑,结合双指针优化效率,并通过严格去重避免重复结果,时间复杂度为 O (n²),空间复杂度为 O (log n)(主要来自排序)。

一、问题理解

问题要求

给定一个整数数组 nums,返回所有和为 0 的三元组 [nums[i], nums[j], nums[k]],满足:

  • i、j、k 是不同的索引(i ≠ j、i ≠ k、j ≠ k);
  • 结果中不能包含重复的三元组(如 [-1, 0, 1] 和 [0, -1, 1] 视为相同,需去重)。

二、核心思路:排序 + 双指针 + 去重

暴力解法(三重循环枚举所有三元组)时间复杂度为 O (n³),且需要额外去重,效率极低。该解法通过以下优化实现高效求解:

  1. 排序:先对数组排序,好处有二:

    • 方便利用双指针缩小查找范围(根据和的大小移动指针);
    • 让相同元素相邻,便于去重(避免重复三元组)
  2. 固定一元素 + 双指针找另外两元素

    • 固定第一个元素 nums[i](i 从 0 到 n-3);
    • 用左指针 left = i+1、右指针 right = n-1 寻找另外两个元素,使得三者和为 0。
  3. 根据和调整指针

    • 若 nums[i] + nums[left] + nums[right] == 0:找到符合条件的三元组,加入结果;
    • 若和 < 0:总和偏小,左指针右移(增大数值);
    • 若和 > 0:总和偏大,右指针左移(减小数值)。
  4. 去重逻辑:对固定元素 i、左指针 left、右指针 right 分别去重,避免重复三元组。

三、代码逐行解析

java

运行

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;class Solution {public List<List<Integer>> threeSum(int[] nums) {// 1. 初始化结果列表(存储所有符合条件的三元组)List<List<Integer>> ans = new ArrayList<>();// 2. 边界处理:数组为空或长度不足3,直接返回空列表if (nums == null || nums.length < 3) {return ans;}// 3. 对数组排序(核心前提:方便双指针查找和去重)Arrays.sort(nums);// 4. 固定第一个元素i,遍历数组(i最大为n-3,确保left和right有位置)for (int i = 0; i < nums.length; i++) {// 4.1 去重i:若当前元素与前一个元素相同,跳过(避免重复三元组)// 注意i>0:防止i=0时越界if (i > 0 && nums[i] == nums[i - 1]) {continue;}// 4.2 初始化双指针:left从i+1开始(避免重复索引),right从末尾开始int left = i + 1;int right = nums.length - 1;// 4.3 双指针循环:left < right(确保两指针不重叠)while (left < right) {// 计算当前三元组的和int sum = nums[i] + nums[left] + nums[right];if (sum == 0) {// 5. 找到符合条件的三元组,加入结果列表ans.add(new ArrayList<>(Arrays.asList(nums[i], nums[left], nums[right])));// 5.1 去重left:跳过与当前left相同的元素(避免重复三元组)// 注意left < right:防止越界while (left < right && nums[left] == nums[left + 1]) {left++;}// 5.2 去重right:跳过与当前right相同的元素while (left < right && nums[right] == nums[right - 1]) {right--;}// 5.3 移动双指针(找到一组后必须移动,避免死循环)left++;right--;} else if (sum < 0) {// 6. 和偏小:左指针右移(增大数值,让总和更接近0)left++;} else {// 7. 和偏大:右指针左移(减小数值,让总和更接近0)right--;}}}// 8. 返回所有符合条件的三元组return ans;}
}

四、实例演示(直观理解过程)

以测试用例 nums = [-1, 0, 1, 2, -1, -4] 为例,演示执行过程:

步骤 1:排序数组

排序后 nums = [-4, -1, -1, 0, 1, 2](相同元素相邻,方便去重)。

步骤 2:遍历固定元素 i

i=0(nums[i] = -4):
  • left=1(-1),right=5(2),sum = -4 + (-1) + 2 = -3 < 0 → left 右移至 2(-1);
  • sum = -4 + (-1) + 2 = -3 < 0 → left 右移至 3(0);
  • sum = -4 + 0 + 2 = -2 < 0 → left 右移至 4(1);
  • sum = -4 + 1 + 2 = -1 < 0 → left 右移至 5,此时 left 不小于 right,循环结束(无符合条件的三元组)。
i=1(nums[i] = -1):
  • 去重检查:i=1 > 0,nums [1](-1)≠ nums [0](-4)→ 不跳过;
  • left=2(-1),right=5(2),sum = -1 + (-1) + 2 = 0 → 符合条件,加入结果 [-1, -1, 2]
    • 去重 left:nums [2](-1)== nums [3](0)?否 → 不移动;
    • 去重 right:nums [5](2)== nums [4](1)?否 → 不移动;
    • 移动指针:left=3,right=4;
  • sum = -1 + 0 + 1 = 0 → 符合条件,加入结果 [-1, 0, 1]
    • 去重 left:nums [3](0)== nums [4](1)?否 → 不移动;
    • 去重 right:nums [4](1)== nums [3](0)?否 → 不移动;
    • 移动指针:left=4,right=3 → 循环结束。
i=2(nums[i] = -1):
  • 去重检查:i=2 > 0,nums [2](-1)== nums [1](-1)→ 跳过(避免重复三元组)。
i=3(nums[i] = 0):
  • left=4(1),right=5(2),sum = 0 + 1 + 2 = 3 > 0 → right 左移至 4,此时 left 不小于 right,循环结束。
i≥4:数组长度不足(left = i+1 会超过 right),循环结束。

最终结果

[[-1, -1, 2], [-1, 0, 1]],无重复三元组,符合要求。

五、关键细节:去重逻辑详解

去重是该题的核心难点,代码通过三层去重确保结果唯一:

  1. 固定元素 i 的去重if (i > 0 && nums[i] == nums[i - 1]) continue;

    • 原因:若 nums [i] 与 nums [i-1] 相同,那么以 i 为第一个元素的三元组,必然和以 i-1 为第一个元素的三元组重复(因为数组已排序,后续元素相同)。
    • 注意 i>0:防止 i=0 时访问 nums [-1] 越界。
  2. 左指针 left 的去重while (left < right && nums[left] == nums[left + 1]) left++;

    • 时机:仅在找到一个符合条件的三元组后执行(避免漏解)。
    • 原因:若 nums [left] 与 nums [left+1] 相同,移动 left 可跳过重复元素,避免同一三元组被多次加入。
    • 注意 left < right:防止 left+1 越界。
  3. 右指针 right 的去重while (left < right && nums[right] == nums[right - 1]) right--;

    • 逻辑同 left 去重,确保 right 侧无重复元素。

六、复杂度分析

  • 时间复杂度:O(n²)。排序耗时 O (n log n),外层循环遍历 i 耗时 O (n),内层双指针遍历耗时 O (n),整体由 O (n²) 主导。
  • 空间复杂度:O(log n)。主要来自排序的空间开销(Java Arrays.sort () 对基本类型使用双轴快排,空间复杂度为 O (log n)),结果列表的空间不计入(属于输出要求)。

七、总结

该解法的核心是 **“排序 + 双指针 + 分层去重”**:通过排序简化查找和去重,双指针将内层查找从 O (n²) 优化为 O (n),分层去重确保结果无重复。这种思路不仅适用于三数之和,还可迁移到 “四数之和” 等类似的 “n 数之和” 问题,是面试中高频考察的经典算法思想。

http://www.dtcms.com/a/581758.html

相关文章:

  • 哪些网站用wordpress建设银行网站总是崩溃
  • c#实现redis的调用与基础类
  • 【深度学习新浪潮】什么是投机解码?大模型推理优化的核心技术解析(含代码实操)
  • Verilog函数function
  • 做电商宠物带哪个网站最好网络营销方法的选择
  • 超融合系统七大核心技术详解
  • Spring Boot 2.7.18(最终 2.x 系列版本)1 - 技术选型:连接池技术选型对比;接口文档技术选型对比
  • 从0到1做一个“字母拼词”Unity小游戏(含源码/GIF)- 单词字母打乱及字母拼词填空逻辑
  • 记一次 Maven 3.8.3 无法下载 HTTP 仓库依赖的排查历程
  • Linux网络初始及网络通信基本原理
  • 免费学软件的自学网站微信app制作
  • Foundation 模态框
  • 赣州深科网站建设深圳商城网站设计电话
  • vllm学习笔记之 PD分离 kv connector
  • 有经验的佛山网站设计东莞华为外包公司
  • 什么是AIGC的创作者?
  • 51单片机基础-GPIO结构详解
  • 织梦系统如何做网站专属头像制作免费
  • 2025高校网络安全管理运维赛--电子取证分析师赛道-决赛WriteUp
  • 蒲公英异地组网路由器全新固件:4G联网、策略路由、日志管理升级
  • 网站建设规划总结做高考题的网站
  • wordpress网站被镜像wordpress邮件功能用不了
  • (111页PPT)智能工厂总体设计方案(附下载方式)
  • sh -c
  • 在若依框架中修改了 Vue 路由的 base 路径后,还需要修改以下几个地方才能正常访问?
  • Spring Boot 注册登录接口进阶(bcrypt密码加密 + Apifox 测试)
  • 重庆住房城乡建设厅官方网站自己做直播网站
  • 服装网站制作网站建设需要的条件
  • 【把Linux“聊”明白】编译器gcc/g++与调试器gdb/cgdb:从编译原理到高效调试
  • LeetCode算法日记 - Day 96: 最长回文子串