LeetCode15. 三数之和

题目

这道题看似和两数之和的的哈希法,但用哈希比较麻烦

分析

双指针

 1 class Solution {
 2 public:
 3     
 4     vector<vector<int>> threeSum(vector<int>& nums) {
 5         vector<vector<int>>res;
 6         sort(nums.begin(),nums.end());
 7         for(int i = 0;i < nums.size();i++){
 8             if(nums[i] > 0 ) return res;
 9             if(i > 0 && nums[i] == nums[i-1]) continue;
10             int left = i+1,right = nums.size()-1;
11             while(left < right){
12                 if(nums[i] + nums[left] + nums[right] > 0) right--;
13                 else if(nums[i] + nums[left] + nums[right] < 0) left++;
14                 else{
15                     res.push_back(vector<int>{nums[i],nums[left],nums[right]});
16                     while(left < right && nums[right] == nums[right-1]) right--;
17                     while(left < right && nums[left] == nums[left+1]) left++;
18                     left++;
19                     right--;
20                 }
21             }
22 
23         }
24 
25         return res;
26         
27     }
28 };

时间复杂度O(N2) ,空间复杂度O(logN)。我们忽略存储答案的空间,额外的排序的空间复杂度为 O(logN)。我们修改了输入的数组  nums,在实际情况下不一定允许,因此也可以看成使用了一个额外的数组存储了 nums 的副本并进行排序,空间复杂度为 O(logN)

更新 yxc 做法:

 1 class Solution {
 2 public:
 3     vector<vector<int>> threeSum(vector<int>& nums) {
 4         vector<vector<int>>res;
 5         
 6         //要用双指针,一定要有序
 7         sort(nums.begin(),nums.end());
 8 
 9         //定义 i < j < k
10         //我们的目的是 找到找到一个最小的 k ,使得 nums[i] + nums[j] + nums[k] >= 0
11         //所以我们可以暴力搜索,就是 O(n三方)
12         //我们没有三指针算法,但有双指针算法,所以可以先枚举 i ,然后再利用双指针
13         //我们使用双指针的目的是为了减少一层时间复杂度
14         for(int i = 0;i < nums.size();i++){
15             if(i > 0 && nums[i] == nums[i-1]) continue;  //去掉 i 重复
16 
17             for(int j = i + 1,k = nums.size()-1;j < k;j++ ){
18                 if(j > i + 1 && nums[j] == nums[j-1]) continue;  //去掉 j 重复,k不需要判断重复,因为 k 由 i,j决定
19                 while(j < k - 1 && nums[i] + nums[j] + nums[k-1] >= 0) k--;  
20                 //这里闫老师写的很妙,采用的是试探法,k-1 为下一个试探元素,首先 j 和 k - 1不重复,
21                 //并且如果 nums[i] + nums[j] + nums[k-1]之和 》0 那么 当前 k 就可以左移
22                 if(nums[i] + nums[j] + nums[k] == 0) res.push_back({nums[i],nums[j],nums[k]});
23             }
24         }
25         return res;
26     }
27 };

 

 

注意:

1. 第九行的去重逻辑应该是如果该元素和上一个元素相同,那么就不考虑这个元素。去重逻辑不应该是下一个元素和当前相同,如果

这样考虑将会导致忽略开头元素相同的情况,如-1,-1,2

2. 第十六十七行的去重逻辑应该再找到一个三元组后,而不应该放在之前

3. 双指针之前一般要排序。

4. 三数之和可以用双指针,那么LeetCode1.两数之和可不可以用双指针呢 ?怎么改就能用了呢?

    因为两数之和那个题是要返回索引和数值所以不能用双指针,用map哈希。若按照本题将两数之和改成返回数组元素,则可以用双指

针,代码如下。

 1   vector<vector<int>> tSum(vector<int>& nums, int target) {
 2         vector<vector<int>> res;
 3         sort(nums.begin(),nums.end());
 4         int left = 0,right = nums.size()-1;
 5         while(left < right){
 6             if(nums[left] > targrt || nums[right] < target ) return res;
 7             if(nums[left] + nums[right] < target){left++;}
 8             else if(nums[left] + nums[right] > target) {right--;}
 9             else { 
10                 res.push_back(vector<int>{nums[left],nums[right]});
11                 while(left < right && nums[right] == nums[right-1]) right--;
12                 while(left < right && nums[left] == nums[left+1]) left++;
13                 right--;
14                 left++;
15             }
16         }        
17         return res;
18     }

这里几乎思路一样,只用外部一个循环就可以。

时间复杂度为O(nlogn),因为找两数的循环的时间复杂度为O(n),C++stl中的排序为改进后的快速排序时间复杂度为O(nlogn),所以取排序时间复杂度

这样可以看出在求解两数之和问题时,一般用哈希,因为若用双指针时间复杂度变高了。

 

posted @ 2021-01-19 16:06  Uitachi  阅读(61)  评论(0编辑  收藏  举报