【中等】15-三数之和 3Sum
题目
Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note:
The solution set must not contain duplicate triplets.
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
Example:
Given array nums = [-1, 0, 1, 2, -1, -4],
A solution set is:
[
[-1, 0, 1],
[-1, -1, 2]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum
解法
方法一:双指针
解题思路
对有序数组求两数之和为某数的算法我们知道,即双指针,从两端取数,和不够就小的一侧指针向中间移,超出则大的一侧向中间移。对于三数也是一样,先对数组进行排序,若三数中包含了第n位数字t,那么只需要对剩下的数字计算两数之和为-t的情况即可,即n从0开始递增,对剩下的n+1位开始的数组求和位-t。
为了剪枝,我们可以判断如果这个第n个数已经大于0,后面的数一定也大于0,和不可能小于0,就直接返回。
代码
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> res;
int size = nums.size();
if(size < 3){
return res;
}
sort(nums.begin(), nums.end());
for(int first = 0; first < size-2; ++first){
if(nums[first] > 0){
return res;
}
if(first == 0 || nums[first] != nums[first-1]){
int left = first+1, right = size-1;
while(left < right){
if(nums[left] + nums[right] + nums[first] > 0)
do --right; while(right>left && nums[right] == nums[right+1]);
else if(nums[left] + nums[right] + nums[first] < 0)
do ++left; while(left < right && nums[left] == nums[left-1]);
else{
vector<int> newinsert = {nums[first], nums[left], nums[right]};
res.push_back(newinsert);
do --right; while(right>0 && nums[right] == nums[right+1]);
do ++left; while(left < size && nums[left] == nums[left-1]);
}
}
}
}
return res;
}
};
方法二:map
解法
用一个map记录每一个数字出现的次数,仍旧是双指针从两端开始,我们已知两个指针的和,只需要判断是否这个和的相反数是否在数组中仍然出现即可。
代码
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
if(nums.size() < 3) return result;
map<int,int> num_map;
map<int,int>::iterator start, end, target;
for (int i=0;i<nums.size();i++) {
if (num_map.find(nums[i]) == num_map.end()) num_map[nums[i]] = 1;
else num_map[nums[i]] += 1;
}
for(start = num_map.begin(); start != num_map.end() && start->first <= 0; ++start) {
start->second--;
end = num_map.end();
end--;
int t = 0- start->first-end->first;
while (start->first<=end->first&&t<=end->first) {
// cout << start->first << " " << t << " " << end->first << endl;
end->second--;
target = num_map.find(t);
if (target!=num_map.end() && target->second>0) {
result.push_back(vector<int>{ start->first, t, end->first});
// cout << "found!" << endl;
}
end->second++;
if(start==end) break;
end--;
t = 0-start->first-end->first;
}
start->second = 0;
}
return result;
}
};
总结
leetcode有个快速解法是对数字进行计数排序,相当于快速查找某个值是否在数组中
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> res;
if (nums.size() < 3) {
return res;
}
int maxNum = 0;
int minNum = 0;
for (const int d : nums) {
maxNum = maxNum < d ? d : maxNum;
minNum = minNum > d ? d : minNum;
}
const int tb_size = maxNum - minNum + 1;
vector<char> rec(tb_size, 0);
for (const int d : nums) {
if (++rec[d - minNum] > 3) {
rec[d - minNum] = 3;
}
}
if (rec[-minNum] == 2) {
rec[-minNum] = 1;
}
vector<int> v;
for (int i = 0; i < tb_size; ++i)
if (rec[i] > 0) {
v.push_back(i + minNum);
}
const int vsz = v.size();
for (int i = 0; i < vsz; ++i) {
const int vmin = v[i];
if (vmin > 0) {
break;
}
for (int j = i; j < vsz; ++j) {
const int vmid = v[j];
const int target = -vmin - vmid;
if (target < vmid) {
break;
}
if (target > maxNum || rec[target - minNum] == 0) {
continue;
}
if (rec[vmid - minNum] > 1 || vmin < vmid && vmid < target) {
res.push_back({vmin, vmid, target});
}
}
}
return res;
}
};
static int _IO_ = []() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
//cout.tie(nullptr);
return 0;
}();
Email:1252418308@qq.com