15&16-三数之和问题
三数之和问题是经典型两数之和的问题的升级版,用常规思路来看,两数之和需要O(n2),三数之和需要O(n3)。
显然有复杂度更简单的方法:排序+双指针。
对排序好的数组,我们将有序的三个数最左边(A)就是最小的放在一边,然后将中间的数(B)和最右边的数(C)分别用指针指向,显然B最小从A的下一位数开始,C最大从数组末尾最大的数开始,分别计算三个数的sum:
1、如果大于0,说明C的值偏大,向左以为让C小点,
2、反之小于0,B就向右一位
3、三数之和等于0,返回结果。
如果没有结果,就把A向右挪一位,换个数,重新执行上述循环,尝试ABC其他的组合,寻求正确结果,A 在数组中有nums.size()-1次尝试,故有nums.size()-1次循环。这里面需要注意的一个问题是,ABC三数之和为0,其中A的值必然是小于等于0的,如果A大于0,A是最小值,三数之和必不存在;A=0,唯一存在的可能就是三数都为0,所以执行算法的时候加上这一判断可以减少很多无必要的循环。这也是需要对数组有序排列的一个原因。
这里面还要注意的一个问题是,返回不重复的组合,举例如下{-1,-1,0,1,}。如果执行上述算法思路,会得到{-1,0,1}{-1,0,1}两个结果,因为-1在数组中出现了两次,每次都对应了这种可能,所以我们还要删除存在的重复值。
这里面其中一个思路是用到哈希表不让重复的元素进来。
另一个思路是对A执行下一位的时候进行判断,如果A和之前的A值是一样的话(就如同上述例子中A执行到-1.下一个还是-1)此时就会遇到相同的结果,我们直接进行下一次循环(将A指向0),便可排除重复的数组。
这个题仔细想想需要很多的细节实现,不是一点点就可以搞定的,所以我们在做的时候要注意很多细节,包括对数组的有序处理方式,需要多做题多谢代码感受一下。
1 class Solution { 2 public: 3 vector<vector<int>> threeSum(vector<int>& nums) { 4 int target; 5 vector<vector<int>> ans; 6 sort(nums.begin(), nums.end()); 7 for (int i = 0; i < nums.size(); i++) { 8 if (i > 0 && nums[i] == nums[i - 1]) continue; 9 if ((target = nums[i]) > 0) break; 10 int l = i + 1, r = nums.size() - 1; 11 while (l < r) { 12 if (nums[l] + nums[r] + target < 0) ++l; 13 else if (nums[l] + nums[r] + target > 0) --r; 14 else { 15 ans.push_back({target, nums[l], nums[r]}); 16 ++l, --r; 17 while (l < r && nums[l] == nums[l - 1]) ++l; 18 while (l < r && nums[r] == nums[r + 1]) --r; 19 } 20 } 21 } 22 return ans; 23 } 24 };