LeeCode数组第15题三数之和

题目:三数之和

内容

给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。

注意:答案中不可以包含重复的三元组。

例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]
思路
题目实现可分为两个步骤,分别是(1)寻找三个满足条件的元素(2)去重复
对于第一个小问题,首先考虑三个for循环,直接寻找
1.第一个数字(num1)选取范围1~n
2.第二个数字(num2)选取范围num1+1~n
3.第三个数字(num3)选取范围num2+1~n
代码如下:
 1 //寻找三数之和为0的数 C++
 2             vector<int> vec(3);
 3             unsigned int num1 = 0, num2 = 0, num3 = 0;
 4             for (num1; num1 < nums.size() - 2; num1++){
 5                 for (num2 = num1 + 1; num2 < nums.size() - 1; num2++){
 6                     for (num3 = num2 + 1; num3 < nums.size(); num3++){
 7                         if (nums[num1] + nums[num2] + nums[num3] == 0){
 8                             vec[0] = nums[num1];
 9                             vec[1] = nums[num2];
10                             vec[2] = nums[num3];
11                             vecs.push_back(vec);
12                         }
13                     }
14                 }
15             }

第二个小问题去重复

C++ 的STL提供有去重算法unique,直接去重即可

1.在寻找满足条件的三个数字之前要先排序,防止相同的vec因为内部元素顺序不同去不了重复,如[1,2,3]和[2,1,3]会判定为不重复

2.对于vecs结果进行去重,去重之前一定要再次排序,因为unique函数只是比较相邻的两个元素是否重复,如果重复就将重复的放到尾部,如果不限排序,对于vecs[[1,2,3],[0,0,0],[1,2,3]],因为相邻的元素都不想等([1,2,3]≠[0,0,0]),系统会去重失败

去重代码如下:

1 //先排序,方便去重
2             sort(nums.begin(), nums.end());
3 //寻找三个满足条件的数字代码省略。。。。
4 //去重
5             sort(vecs.begin(), vecs.end());
6             vecs.erase(unique(vecs.begin(), vecs.end()), vecs.end());
7 
8             return vecs;

此外,还要考虑异常的情况,当传入的数组长度小于3时,无法给出满足条件的解,应返回空的容器

完整代码如下:

 1 vector<vector<int>> threeSum(vector<int>& nums) {
 2             vector<vector<int>> vecs;
 3             //异常判断
 4             if (nums.size() < 3)
 5                 return vecs;
 6 
 7             //先排序,方便去重
 8             sort(nums.begin(), nums.end());
 9 
10             //寻找三数之和为0的数
11             vector<int> vec(3);
12             unsigned int num1 = 0, num2 = 0, num3 = 0;
13             for (num1; num1 < nums.size() - 2; num1++){
14                 for (num2 = num1 + 1; num2 < nums.size() - 1; num2++){
15                     for (num3 = num2 + 1; num3 < nums.size(); num3++){
16                         if (nums[num1] + nums[num2] + nums[num3] == 0){
17                             vec[0] = nums[num1];
18                             vec[1] = nums[num2];
19                             vec[2] = nums[num3];
20                             vecs.push_back(vec);
21                         }
22                     }
23                 }
24             }
25 
26             //去重
27             sort(vecs.begin(), vecs.end());
28             vecs.erase(unique(vecs.begin(), vecs.end()), vecs.end());
29 
30             return vecs;
31     }

对于较短的输入,能给出合适的结果,然后对于巨长的数组,系统提示运算时间过长,因此需要优化代码。

根据题目所给的条件,可以看出三个数字之和是定值,因此,当我们选取第一个数字num1后,问题变为寻找和为0-num1的两个数字。

可以观察到,因为数组是有序的,我们可以设置两个指针从两边同时选取

 1 int targetSum = 0 - nums[num1];
 2             while (pLeft < pRight ){
 3                 int sum = nums[pLeft] + nums[pRight];
 4                 if (sum > targetSum || nums[pRight] == nums[pRight-1]){
 5                     pRight--;
 6                 }
 7                 else if (sum < targetSum || nums[pLeft] == nums[pLeft - 1]){
 8                     pLeft++;
 9                 }
10                 else{
11                     vec[0] = nums[num1];
12                     vec[1] = nums[pLeft];
13                     vec[2] = nums[pRight];
14                     vecs.push_back(vec);
15                     pLeft++;
16                     pRight--;
17                 }
18             }

也因为数组是排序的,为了如果我们选取的第一个数子大于0,则后两个必然大于0,可以跳出循环

对于重复的数字,我们可以选择跳过

完整代码如下:

 1 vector<vector<int>> threeSum(vector<int>& nums) {
 2         vector<vector<int>> vecs;
 3         //异常判断
 4         if (nums.size() < 3)
 5             return vecs;
 6 
 7         //先排序,方便去重
 8         sort(nums.begin(), nums.end());
 9         
10         //寻找三数之和为0的数
11         vector<int> vec(3);
12         unsigned int num1 = 0, num2 = 0, num3 = 0;
13         for (num1; num1 < nums.size() - 2; num1++){
14             if (nums[num1] * 3 > 0)//数组是从小到大排序
15                 break;
16             if (num1 != 0 && nums[num1] == nums[num1 - 1])
17                 continue;
18 
19             int pLeft, pRight;
20             pLeft = num1+1;
21             pRight = nums.size() - 1;
22             
23             int targetSum = 0 - nums[num1];
24             while (pLeft < pRight ){
25                 int sum = nums[pLeft] + nums[pRight];
26                 if (sum > targetSum || nums[pRight] == nums[pRight-1]){
27                     pRight--;
28                 }
29                 else if (sum < targetSum || nums[pLeft] == nums[pLeft - 1]){
30                     pLeft++;
31                 }
32                 else{
33                     vec[0] = nums[num1];
34                     vec[1] = nums[pLeft];
35                     vec[2] = nums[pRight];
36                     vecs.push_back(vec);
37                     pLeft++;
38                     pRight--;
39                 }
40             }
41         }
42 
43         //去重
44         sort(vecs.begin(), vecs.end());
45         vecs.erase(unique(vecs.begin(), vecs.end()), vecs.end());
46 
47 
48         return vecs;
49     }

对于复杂测试案例的运行时间是60ms,通过题目!



转载于:https://www.cnblogs.com/feichangnice/p/9025491.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值