15. 三数之和
题目描述
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。注意:答案中不可以包含重复的三元组。
解题思路
以常规思路,就是使用三重循环来依次枚举三元组<a,b,c>,重点是如何保证不重复。
解决不重复问题:
- 首先对数组从小到大排序,这样能保证有a <= b <= c,这样就能保证只有<a,b,c>这个顺序的三元组能枚举到,而<c,b,a>,<b,a,c>等这些不会被枚举到;
- 然后,对于每一层循环中枚举的元素,需要保证相邻两次枚举的元素不相同,否则也会造成重复;
使用三层循环会造成超时,如何降低时间复杂度?
固定前两个枚举的元素a与b,后面满足 a+b+c = 0条件的元素c是唯一的(前面保证了每一层循环中相邻两次枚举的元素不相同)。那么当第二层循环往后枚举下一个元素b',由于b'>b,要想找到满足a+b'+c'条件的元素c',必然有c'<c,也即元素c'必然出现在元素c的左侧。因此我们可以使用双指针,在从前往后枚举元素b的同时,从后往前枚举元素c,均摊下来,可以使时间复杂度降低到O(n2)。
代码实现:
/** * @param {number[]} nums * @return {number[][]} */ var threeSum = function(nums) { nums.sort((a,b)=>a-b); const ans = []; /** * i指向枚举元素a, j指向枚举元素b,k指向枚举元素c */ for(let i=0;i<nums.length;i++){ //从第二个元素开始,保证每次枚举时当前元素与前一次枚举的元素不相同。相同则跳过本次循环 if(i>0&&nums[i]===nums[i-1]){ continue; } let k=nums.length-1; //第一层循环应初始化元素c对应的指针k指向最后一个元素 for(let j=i+1;j<nums.length;j++){ //同第一层循环,除了第一次枚举,后面每一次枚举都要保证当前元素与前一次枚举的元素不相同 if(j>i+1&&nums[j]===nums[j-1]){ continue; } //如果a+b+c>0,表示满足条件的元素c应该比当前的要小,往前找 while(j<k&&nums[i]+nums[j]+nums[k]>0){ k--; } //该条件判断要放在while语句之后,防止j与k指向同一个元素,造成额外的错误 if(j>=k){ break; } if(nums[i]+nums[j]+nums[k]===0){ ans.push([nums[i],nums[j],nums[k]]); } } } return ans; };
这里可能会产生一个疑问:为什么第三层循环没有使用条件判断来保证相邻两次枚举的元素不相同?以下面示例进行分析。
示例:[-2,-2,0,1,4,4,6,6]
因为我们在第二层循环时已经保证了相邻两次枚举的元素不相同,假设我们找到了a+b+c = num[0]+nums[1]+nums[5] = 0,而即使找到了,我们也会在第二层循环中更换元素b,更换了元素b相当于更换了条件a+b'+c'=0,下一次找到的c'必然不可能与此次的c相同,实际上相当于保证了第三次循环中相邻两次枚举的元素不相同。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)