454. 四数相加 II
给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:
0 <= i, j, k, l < n
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
解法:创建一个map用于存放数组1,2的和,即key,以及和出现的次数,即value;再遍历数组1,2,将和对应的位置进行计数;再遍历数组3,4,在map中查找是否存在等于和为0的数,即数组3,4的和的负数;如果key存在,则返回map中对应的value,即这个数出现的次数
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
//定义一个mao表,用于存放数组nums1,nums2的和
unordered_map<int,int> map;
//遍历数组nums1,nums2,记录他们的和,并存入map表中
//在map表中,key是他们的和。value是和出现的次数
for(int a:nums1)
{
for(int b:nums2)
{
map[a+b]++;
}
}
int sum=0; //定义一个计数,统计和为0的个数
//遍历数组nums3,nums4
for(int c:nums3)
{
for(int d:nums4)
{
//如果在map中能找到0-(c+d)的值,就表示在nums1,nums2数组的和中存在与数组nums3,nums4的和为0的key
if(map.find(0-(c+d))!=map.end())
{
//在nums1,nums2的和中找到key,则返回key的value,即nums1,nums2的和出现的次数
sum+=map[0-(c+d)];
}
}
}
return sum; //返回满足和为0的个数
}
};
15. 三数之和
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请
你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
解法:双指针解法
先对数组进行快速排序,sort(nums.begin(),nums.end()),从小到大排序;
三个元素,一个当前数组元素的下标,一个左指针(当前元素的下一个元素的下标),一个右指针(数组的最后一个元素的下标);
当三个元素之和大于0时,表示和过大,因为数组是排序过的,所以应该右指针向前移动一位,让右指针的元素的值减小,使得和减小;当三个元素之和小于0时,表示和过小,所以应该左指针向后移动一位,让左指针的元素的值增加,使得和增加;如果等于0,则写入二维数组result.push_back(vector<int>{a,b,c}),同时双指针收缩
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
//创建一个二维数组
vector<vector<int>> result;
//快速排序,让数组从小到大排序
sort(nums.begin(),nums.end());
//遍历数组,i为当前数组下标
for(int i=0;i<nums.size();i++)
{
//如果排序后的数组第一个元素大于零,则不存在a+b+c等于0的情况了,就直接返回结果
if(nums[i]>0)
{
return result;
}
//去重,如果当前元素等于前一个元素,则跳出当前循环,继续下一个循环
if(i>0 && nums[i]==nums[i-1])
{
continue;
}
//左指针等于当前数组下标的下一个下标
int left=i+1;
//右指针等于数组最后一个元素的下标
int right=nums.size()-1;
//当左右指针不相等时
while(right>left)
{
//如果a+b+c大于0,则表示和太大了,所以右指针向前移动一位,让数组之和减小
if(nums[i]+nums[left]+nums[right]>0)
{
right--;
}
//如果a+b+c小于0,则表示和太小了,所以左指针向后移动一位,让数组之和增加
else if(nums[i]+nums[left]+nums[right]<0)
{
left++;
}
else //如果等于0
{
//将当前的数组元素写入二维数组中
result.push_back(vector<int>{nums[i],nums[left],nums[right]});
// 去重逻辑应该放在找到一个三元组之后,对b 和 c去重
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
// 找到答案时,双指针同时收缩
right--;
left++;
}
}
}
return result;
}
};
18. 四数之和
给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):
0 <= a, b, c, d < n
a、b、c 和 d 互不相同
nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。
解法:双指针解法,与三数之和的思路相同,都是左右指针同时向中间收缩,不同的是,在当前元素的for循环之外再加上一层for循环,即a=当前元素,b=a+1,left=b+1,right=nums.size()-1
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
//创建一个二维数组
vector<vector<int>> result;
//快速排序,从小到大
sort(nums.begin(),nums.end());
//第一层遍历
for(int i=0;i<nums.size();i++)
{
//如果排序后的数组第一个元素大于target,则不存在a+b+c+d等于target的情况了,就直接返回结果
if(nums[i]>target && nums[i]>=0)
{
break;
}
//去重
if(i>0 && nums[i]==nums[i-1])
{
continue;
}
//第二层遍历
for(int j=i+1;j<nums.size();j++)
{
//如果前两个元素之和大于target,则不存在a+b+c+d等于target的情况了,就直接返回结果
if (nums[j] + nums[i] > target && nums[j] + nums[i] >= 0) {
break;
}
// 对nums[j]去重
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
//定义左右指针
int left=j+1;
int right=nums.size()-1;
while(right>left)
{
//和大于target,右指针收缩
if((long) nums[i]+nums[j]+nums[left]+nums[right]>target) right--;
//和小于target,左指针收缩
else if((long) nums[i]+nums[j]+nums[left]+nums[right]<target) left++;
else
{
//和等于target,写入二维数组
result.push_back(vector<int>{nums[i],nums[j],nums[left],nums[right]});
//去重
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
//左右指针同时收缩
left++;
right--;
}
}
}
}
return result;
}
};