15. 三数之和
难度中等4356收藏分享切换为英文接收动态反馈
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
示例 2:
输入:nums = []
输出:[]
示例 3:
输入:nums = [0]
输出:[]
题目思路:与两数之和不同的是这道题如果纯粹用遍历的方法会导致复杂度非常高,因为遍历过程中会出现很多重复的情况。
因此如何消除这些重复的情况成为我们优化的方向。本方法采用的是双指针+排序的方法
1.我们先将数据从小到大排序
2.然后构建l 和 r两个指针,一个是从 i+1 开始 一个从n-1开始 因为数组是从小到大排列的 因此
当nums[i] + nums[l] + nums[r] > 0时,r 应该左移 变小 从而继续尝试 同理 nums[i] + nums[l] + nums[r] < 0
l应该右移 因为数组是从小到大排列的 所以 应该右移
在上述情况中我们应该注意到几种特殊情况
(1)在遍历过程中如果遇到当下的i 和 i-1是一样的 那么没必要继续,直接跳过
或者l he r过程中遇见相同的则直接跳过
(2) 遇见第一个nums[i]为0则直接输出值即可。
python代码:
class Solution: def threeSum(self, nums: List[int]) -> List[List[int]]: n = len(nums) #先求出列表的长度,和建立一个空列表用处存储符合题意的数组 ans = [] nums.sort() #将列表进行从小到大的排序 if n<3: #如果列表小于3则直接输出空列表 return [] for i in range(n): #对列表进行遍历 双指针 if nums[i]>0: #因为是从小到大排序了,所以如果第一个数就大于0,则没有遍历的必要了 直接输出 return ans if i>0 and nums[i] == nums[i-1]: #如果这个数跟上一个的一样,则没有必要继续,直接下一个 continue #直接跳到下次循环 r , l = n - 1 , i + 1 #构建双指针的左指针#构建双指针的右指针 while l<r: #循环条件左指针必须永远小于右指针 if nums[i] + nums[l] + nums[r] > 0: #如果三个相加大于0,则 说明R需要左移,因为排序过了 从小到大 r -= 1 elif nums[i] + nums[l] + nums[r] < 0:#如果三个相加大于0,则 说明l需要右移,因为排序过了 从小到大 l += 1 elif nums[i] + nums[l] + nums[r] == 0: #如果等于0则保存结果 ans.append([nums[i] , nums[l] , nums[r]]) while(l<r and nums[l] == nums[l+1]):#如果nums[l] == nums[l+1] 则直接跳过这个 进行下一个 避免重复 l += 1 while(l<r and nums[r] == nums[r-1]):#如果nums[r] == nums[r-1] 则直接跳过这个 进行下一个 避免重复 r -= 1 l += 1 r -= 1 return ans #输出结果
C++代码:
class Solution { public: vector<vector<int>> threeSum(vector<int>& nums) { vector<vector<int> > result;//构建空列表用来存放最后的输出的结果 sort(nums.begin() , nums.end());//排序 将数组进行从小到大排序 for (int i=0; i<nums.size();i++){ //遍历数组 if (nums[i]>0){ //如果大于0则说明三数之和必定大于0则没有遍历的必要了之后结束 return result; } if (i>0 && nums[i]==nums[i-1]){//如果当前的值等于前一个 则跳过进行下一个循环,避免重复 continue; //跳过进入下一个循环 } int l = i+1 , r = nums.size()-1;//定义左右双指针 while (l<r){ if ((nums[i] + nums[l] + nums[r])>0){//如果大于0则是说明大了,则右指针r需要左移 r--;//右指针左移 } else if ((nums[i] + nums[l] + nums[r])<0){//同理如果小于0则是说明小了,则右指针l需要右移 l++;//左指针右移 } else if ((nums[i] + nums[l] + nums[r])==0){ //如果等于0则 result.push_back({nums[i] , nums[l] , nums[r]}); //则将这组值存在下 //插入result我们之前准备好的数组里面 while(l<r && nums[l]==nums[l+1]){ //检查下一个l跟之前的一样不 一样直接跳过 l++; } while(l<r && nums[r]==nums[r-1]){//同理检查下一个r跟之前的一样不 一样直接跳过 r--; } l++,r--; } } } return result; } };