【LeetCode-数组】三数之和
题目描述
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
题目链接:https://leetcode-cn.com/problems/3sum/
思路1
比较基础的方法,先找到两个数,然后固定住这两个数去寻找满足条件的第三个数,为了加快寻找速度可以通过哈希表来寻找。代码如下:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> ans;
if(nums.empty())
return ans;
map<int, int> hashTabel;
for(int i=0; i<nums.size(); i++)
hashTabel[nums[i]] = i;
for(int i=0; i<nums.size(); i++){
for(int j=i; j<nums.size(); j++){
int complement = -nums[i]-nums[j];
if(i!=j && hashTabel.find(complement)!=hashTabel.end()
&& hashTabel[complement]!=i && hashTabel[complement]!=j){
ans.push_back({nums[i], nums[j], nums[hashTabel[complement]]});
}
}
}
// 使用set去重
for(int i=0; i<ans.size(); i++)
sort(ans[i].begin(), ans[i].end());
set<vector<int>> s(ans.begin(), ans.end());
ans.assign(s.begin(), s.end());
return ans;
}
};
// 超时
这样的话得到的结果里面包含重复的三元组,所以使用set去重后返回。
- 时间复杂度O(n^2)
包含一个双层循环。 - 空间复杂度O(n)
使用了哈希表。
该方法由于超时未通过。
思路2
假如是在排序数组中找和为target的两个数,则我们可以使用两个指针,一个指针left从左到右遍历,另一个指针right从右到左遍历。如果两个指针指向的两个数字之和小于target,则left++;如果大于target,则right--。直至找到target或者left>=right。代码如下:
//array = [1,2,3,4,5,6,7,8,9]; array is sorted in a increasing order
//target = 10;
int len = array.size();
int p1 = 0, p2 = len - 1; //p1 points to the start of the array and p2 points to the end
//the search stops when the two pointers meet each other
while(p1 < p2){
if(array[p1] + array[p2] < target){
p1++;
}else if(array[p1] + array[p2] == target){
cout << p1 << p2 << endl;
p1++,p2--;
}else{
p2--;
}
}
对于这个问题,我们可以用类似的思想。首先,将数组排序,然后从头开始遍历数组,当遍历到nums[i]时,我们从i+1开始往后利用上面的方法寻找两个值使得两个值之和为-nums[i]即可。
还有一个问题就是去重,例如输入为[-1,-1,0,0,1,1],算法结果中会包含两个[-1,0,1]。解决这个问题的办法是:当移动指针的时候,要移动到一个位置,这个位置和指针移动前指向的值不同,而不是简单地将指针移动一步。代码如下:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> ans;
if(nums.empty() || nums.size()<3)
return ans;
sort(nums.begin(), nums.end());
for(int i=0; i<nums.size(); i++){
if(i==0 || (i>0&&nums[i]!=nums[i-1])){
int left = i+1;
int right = nums.size()-1;
while(left<right){
int s = nums[i]+nums[left]+nums[right];
if(s<0){
left++;
}else if(s>0) {
right--;
}else{
ans.push_back({nums[i], nums[left], nums[right]});
while(left<right && nums[left]==nums[left+1]){ // 找到下一个不相等的下标,为了去重
left++;
}
while(left<right && nums[right]==nums[right-1]){ // 找到下一个不相等的下标,为了去重
right--;
}
left++;
right--;
}
}
}
}
return ans;
}
};
- 时间复杂度O(n^2)
- 空间复杂度O(n)
总结
双指针的应用场景:去重、找满足条件的数字、链表相关问题等。
参考
1、https://leetcode-cn.com/problems/3sum/solution/xiang-xi-jie-shi-shuang-zhi-zhen-jie-jue-san-shu-z/
2、https://leetcode-cn.com/problems/3sum/solution/pai-xu-shuang-zhi-zhen-zhu-xing-jie-shi-python3-by/