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),所以取排序时间复杂度
这样可以看出在求解两数之和问题时,一般用哈希,因为若用双指针时间复杂度变高了。