这两题看似相似,其实差的很多,第454不需要考虑去重,但是第15题需要考虑去重问题,所以两题方法是不一样的
LeetCode第454四数相加
这道题目我们用哈希表做比较简单,原因是这是四个独立的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不用考虑有重复的四个元素相加等于0的情况
本题注意事项:
一.基于哈希表实现的两个主要容器:
unordered_map:
存储键值对(key-value pairs)。
每个键是唯一的,每个键对应一个值。
用于快速查找特定键并获取相应值的场景,如构建字典、缓存等
基于哈希表实现,平均查找时间复杂度是 O(1)。
unordered_map的方法:
1. `insert(key, value)`:插入键-值对。
2. `erase(key)`:删除具有给定键的元素。
3. `find(key)`:查找具有给定键的元素,并返回指向该元素的迭代器。
4. `count(key)`:返回具有给定键的元素的数量(通常为0或1,因为键必须唯一)。
5. `size()`:返回容器中的元素数量。
6. `empty()`:检查容器是否为空。
7. `clear()`:清空容器中的所有元素。
8. `begin()` 和 `end()`:迭代容器的起始和结束位置。
unordered_set:
存储唯一值的集合。
用于存储一组元素,并确保其中不包含重复值的场景,如查找元素是否存在、去重、存储集合等。
基于哈希表实现,平均查找、插入和删除时间复杂度都是 O(1)。
`std::unordered_map` 和 `std::unordered_set` 是C++中的无序关联容器,它们分别用于存储键-值对和唯一值的集合。
unordered_set的方法:
1. `insert(value)`:插入值。
2. `erase(value)`:删除具有给定值的元素。
3. `find(value)`:查找具有给定值的元素,并返回指向该元素的迭代器。
4. `count(value)`:返回具有给定值的元素的数量(通常为0或1,因为值必须唯一)。
5. `size()`:返回容器中的元素数量。
6. `empty()`:检查容器是否为空。
7. `clear()`:清空容器中的所有元素。
8. `begin()` 和 `end()`:迭代容器的起始和结束位置。
请注意,由于这些容器是无序的,元素的顺序是不确定的。
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int,int>umap;//key:a+b的数值,value:a+b数值出现的次数
//遍历nums1和nums2,统计两个数组元素之和和出现的次数,放到map中
for(int a:nums1){
for(int b:nums2)
{
umap[a+b]++;
}
}
int count=0;//统计a+b+c+d=0出现次数
//在遍历大C和大D数组,找到如果0-(c+d)在map中key对应的value也就是出现次数统计出来
for(int c:nums3)
{
for(int d:nums4)
{
if(umap.find(0-(c+d))!=umap.end())
{
count+=umap[0-(c+d)];
}
}
}
return count;
}
};
leetcode第15题三数之和
这题我们用双指针去做,给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组,因为是在一个数组中查找,所以必须考虑去重情况,把符合条件的三元组放进vector中,然后再去重,去重的过程不好处理,有很多小细节,如果在面试中很难想到位。
所以这题我们用双指针的思路
解题步骤:
1. 首先,对输入的 `nums` 数组进行排序,这可以帮助我们更容易地处理重复元素和找到三数之和为零的组合。
2. 创建一个空的二维向量 `result`,用于存储找到的三数之和为零的组合。
3. 使用一个循环来遍历 `nums` 数组,以确定第一个元素 `a`。如果 `a` 大于零,则可以直接返回结果,因为不可能存在三数之和为零的组合。
4. 进行正确的去重操作。在这个问题中,需要确保不会重复考虑相同的 `a` 值。通过跳过重复的 `a` 值,可以避免重复的组合。
5. 使用双指针技巧来查找另外两个元素 `b` 和 `c`,它们的和应该等于 `-a`。一个指针 `left` 从当前元素的下一个位置开始,另一个指针 `right` 从数组末尾开始。
6. 在内部循环中,根据 `a + b + c` 的值与零的比较情况,采取不同的行动:
- 如果 `a + b + c` 大于零,则将 `right` 指针向左移动,以减小总和。
- 如果 `a + b + c` 小于零,则将 `left` 指针向右移动,以增加总和。
- 如果 `a + b + c` 等于零,说明找到了一个符合条件的三数之和组合,将其添加到 `result` 中。
7. 在找到一个三数之和为零的组合后,需要进行去重操作,以确保不重复考虑相同的 `b` 和 `c` 值。这是通过在找到答案后,分别移动 `left` 和 `right` 指针来完成的。
8. 最后,继续循环,直到所有可能的组合都被考虑完毕。
9. 返回 `result`,其中包含了所有符合条件的三数之和为零的组合。
这就是解决这个问题的基本思路和步骤。这个算法的关键在于排序、双指针和去重操作,它们共同确保了高效地找到符合条件的三数之和为零的组合。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
// 找出a + b + c = 0
// a = nums[i], b = nums[left], c = nums[right]
for (int i = 0; i < nums.size(); i++) {
// 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了
if (nums[i] > 0) {
return result;
}
// 错误去重a方法,将会漏掉-1,-1,2 这种情况
/*
if (nums[i] == nums[i + 1]) {
continue;
}
*/
// 正确去重a方法
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
int left = i + 1;
int right = nums.size() - 1;
while (right > left) {
// 去重复逻辑如果放在这里,0,0,0 的情况,可能直接导致 right<=left 了,从而漏掉了 0,0,0 这种三元组
/*
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
*/
if (nums[i] + nums[left] + nums[right] > 0) right--;
else if (nums[i] + nums[left] + nums[right] < 0) left++;
else {
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;
}
};