news 2026/6/26 7:26:03

hot100 三数之和(15)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
hot100 三数之和(15)

一、 算法核心思想

“三数之和”最大的难点不在于找出组合,而在于如何高效地去除重复解(即答案中不能包含重复的三元组)。该算法通过以下三个步骤精妙地解决了这个问题:

  1. 预处理(排序):首先对整个数组进行升序排序。排序的作用有两个:

    • 允许我们使用双指针从两端向中间靠拢(单调性)。

    • 将相同的数字聚集在一起,为后续的去重操作打下基础。

  2. 固定基准数(外层循环):遍历数组,以当前元素nums[i]作为三元组的第一个数。

  3. 双指针扫描(内层循环):在nums[i]右侧的剩余区间[i + 1, n - 1]内,设置左指针left和右指针right

    • 如果三个数之和等于 0,记录结果。

    • 如果和小于 0,说明数据太小,左指针右移(left++)。

    • 如果和大于 0,说明数据太大,右指针左移(right--)。

二、 代码逐行拆解与去重细节

以下是该解法的源码及深度注释,重点解析了算法中的三次去重逻辑。

class Solution { public List<List<Integer>> threeSum(int[] nums) { // 1. 关键步骤:先对数组进行升序排序 Arrays.sort(nums); List<List<Integer>> ans = new ArrayList<>(); int n = nums.length; // 2. 外层循环:固定三元组的第一个元素 nums[i] for (int i = 0; i < n - 2; i++) { // 【第一处去重】:如果当前元素和上一个元素相同,直接跳过 // 思考:为什么是和 nums[i-1] 比,而不是和 nums[i+1] 比? // 答:因为 nums[i-1] 已经作为第一个数遍历过了,所有包含它的组合都已找完。 // 如果和 nums[i+1] 比,会把类似 [-1, -1, 2] 这种合法的包含重复元素的三元组错误地漏掉。 if (i >= 1 && nums[i] == nums[i - 1]) { continue; } // 3. 初始化双指针 int left = i + 1; // 两个指针只在固定的基准数右侧移动 int right = n - 1; int target = -nums[i]; // 我们需要满足 nums[left] + nums[right] == target // 4. 内层循环:双指针向中间靠拢 while (left < right) { int sum = nums[left] + nums[right]; if (sum == target) { // 找到满足条件的三元组,存入结果集 ans.add(List.of(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 ans; } }

三、 面试官常问:去重逻辑深度剖析

这道题在 CSDN 或面试中最有价值的部分就是去重细节

1. 为什么外层去重要用nums[i] == nums[i-1]

如果我们写成nums[i] == nums[i+1],在面对输入[-1, -1, 2]时:

  • i = 0nums[0] = -1)时,算法发现nums[0] == nums[1],直接continue跳过。

  • 这样就会导致[-1, -1, 2]这个唯一正确的解被丢弃。

  • 正确逻辑:我们应该允许同一个三元组内部包含重复数字(如nums[0]nums[1]),但不能允许两个不同三元组的首元素在不同轮次里扮演相同的角色。因此,通过与历史过去的nums[i-1]对比,能确保“用过即丢弃”。

2. 内层双指针找到解后,为什么要一边执行while去重,最后还要执行left++right--

sum == target时,如果不去重,leftright仅仅各移动一步,如果移动到的新位置数字和刚才一模一样,就会产生一模一样的重复三元组。

  • 内部的while循环是负责把指针推到重复数字的最后一个位置

  • 随后的left++right--才是真正让指针跨越到全新数字的关键一步。

四、 复杂度分析

1. 时间复杂度

  • 数组排序Arrays.sort(nums)消耗O(n log n)时间。

  • 双层循环扫描

    • 外层循环执行了n次。

    • 内层双指针leftright在每一轮外层循环中,合起来最多从两端走到中间,即最多移动n次。

    • 嵌套循环的时间复杂度为O(n * n) = O(n^2)

  • 总时间复杂度O(n log n) + O(n^2) = O(n^2)。相比于暴力三层循环的O(n^3),这是一个质的飞跃。

2. 空间复杂度

  • 辅助空间:在算法执行过程中,除了存储答案的ans之外,我们只使用了leftrightsumtarget等几个常数级别的变量,它们占用O(1)的空间。

  • 排序空间:Java 中Arrays.sort()对对象或基元类型的排序(如快速排序、TimSort)会消耗O(log n)O(n)的隐式栈空间。

  • 总空间复杂度:忽略输出结果集,算法的执行空间复杂度为O(log n)(主要由排序算法的栈空间决定)。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/26 7:25:26

2026数字化农业:水溶肥科学选配指南,助力高产优质

数字化农业时代的高效水溶肥应用技术解析在当前农业数字化转型背景下&#xff0c;科学施肥技术正经历着前所未有的革新。作为农业生产的重要投入品&#xff0c;水溶肥料的应用已经从简单的营养补充发展为集数据分析、精准施用于一体的系统工程。本文将围绕现代农业对水溶肥料的…

作者头像 李华
网站建设 2026/6/26 7:24:20

从LLM到VLA再到世界模型:2026年基座模型的技术演进路线图

当大模型不再满足于“预测下一个Token”,物理世界的“下一帧”正在成为AI的新战场。 引言:基座模型的“三级跳” 2026年过半,基座模型的技术版图正在经历一场静水深流的变革。 如果说2023年是“百模大战”的元年,2024年是“长上下文”的军备竞赛,2025年是“推理能力”的…

作者头像 李华
网站建设 2026/6/26 7:24:11

Navigation-Learning:一个本科生整理的导航定位学习资料库

文章目录Navigation-Learning&#xff1a;一个本科生整理的导航定位学习资料库仓库里有什么重点介绍的开源项目开源项目记录适合谁用Navigation-Learning&#xff1a;一个本科生整理的导航定位学习资料库 GitHub 上有一个仓库&#xff0c;专门收集导航定位领域的学习资料。目前…

作者头像 李华
网站建设 2026/6/26 7:23:49

【操作系统】进程同步与互斥的基本概念

考点频率&#xff1a;★★★★★&#xff08;必考&#xff0c;PV操作的前置基础&#xff09; 难度&#xff1a;⭐⭐ 建议&#xff1a;掌握同步与互斥的本质区别&#xff0c;理解临界资源的概念1️⃣ 为什么要引入同步与互斥&#xff1f; 在多道程序设计环境下&#xff0c;多个进…

作者头像 李华
网站建设 2026/6/26 7:23:00

铝板加工ERP单据打印技术解析:解决行业单据合规与适配难题

铝板贸易加工行业单据场景复杂&#xff0c;涵盖原料入库单、加工工单、分切出库单、磅单、对账单、质检报表等专属单据&#xff0c;存在规格参数多、理实重换算、批次追溯严、对外对账合规性要求高等特点。传统ERP打印模块通用性强、行业适配弱&#xff0c;普遍出现模板固定、数…

作者头像 李华
网站建设 2026/6/26 7:20:08

Marker Tracking 被动标志点追踪测量

被动标志点测量模块 Marker Tracking集成的离散标志点跟踪测量功能 Intergrated Discrete Marker TrackingVIC-3D 具备在三维空间中追踪离散标记点&#xff08;圆形或领结形&#xff09;的能力。与手动测量相比&#xff0c;该功能可大幅节省时间。此外&#xff0c;它还能精确测…

作者头像 李华