代码随想录训练营打卡DAY 7 | 哈希表+双指针
四数相加
注意:与四数之和区分开
题解
思路:利用unordered_map存储sum1和sum2的和key以及出现的次数value,遍历sum3和sum4与map比较即可。
核心代码【C++】:
// 4个数组,与只有一个数组(四数之和)区分开
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
// int n = nums1.size();
// int i,j,result = 0;
// unordered_map<int,int> sum1_2;
// // 遍历nums1和nums2,求出数组的和 key 并记录个数 value
// for(i = 0; i < n; i++){
// for(j = 0; j < n; j++){
// int sum = nums1[i] + nums2[j];
// auto iter = sum1_2.find(sum);
// if(iter == sum1_2.end()) sum1_2.insert(pair<int,int>(sum,1));
// else iter->second++;
// }
// }
// // 遍历nums3和nums4,看看其和的相反数在不在集合sum1_2中
// for(i = 0; i < n; i++){
// for(j = 0; j < n; j++){
// int target = - nums3[i] - nums4[j];
// auto iter = sum1_2.find(target);
// if(iter != sum1_2.end()) result += iter->second;
// }
// }
// return result;
// c++风格代码
int result = 0;
unordered_map<int,int> sum1_2;
for(int a : nums1){
for(int b : nums2){
// C++中,map[i] 即value
sum1_2[a + b]++;
}
}
for(int c : nums3){
for(int d : nums4){
if(sum1_2.find(-c-d) != sum1_2.end()) result += sum1_2[-c-d];
}
}
return result;
}
时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
空间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
赎金信
题解
思路:利用map存储字母以及出现的次数。由于键值是小写字母,个数有限,所以用数组其实是更好的解法。
核心代码【C++】:
bool canConstruct(string ransomNote, string magazine) {
// 本道题由于键值是小写字母,个数有限,所以用数组其实是更好的解法
unordered_map<char,int> magazineSet;
for(char c : magazine){
magazineSet[c]++;
}
for(char c : ransomNote){
auto iter = magazineSet.find(c);
if(iter == magazineSet.end()) return false;
else{
iter->second--;
if(iter->second == -1) return false;
}
}
return true;
}
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
1
)
O(1)
O(1)
三数之和 & 四数之和
注意:与四数相加区分开
题解
思路:利用双指针,这里只解析三数之和,四数之和同理。
- 双指针法求和前需要排序。
- 一层for循环遍历sum1,由于题目要求不重复,所以当num[i] == num[i-1]时,要跳过当前的sum1;同时这里不可以是num[i] == num[i+1],因为这样其实是比较sum1与sum2了,如[-1,-1,2]。
- sum2与sum3分别用指针left与right指向。当sum == target时将三元组push到result,反之收缩指针left或right。且只有当push后才需要对sum2或sum3去重,具体见代码注释。
核心代码【C++】:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
int i, left, right;
sort(nums.begin(), nums.end());// 将nums排序
int n = nums.size();
for(i = 0; i < n; i++){
left = i + 1;
right = n - 1;
if(i > 0 && nums[i] == nums[i - 1]){//对sum1去重
continue;
}
while (left < right)
{
if(nums[i] + nums[left] + nums[right] == 0){
result.push_back(vector<int>{nums[i], nums[left++],nums[right--]});
// 对sum2去重
while (left < right && nums[left] == nums[left-1])
{
left++;
}
// 对sum3去重
while (left < right && nums[right] == nums[right+1])
{
right--;
}
}
else if(nums[i] + nums[left] + nums[right] < 0){
left++;
// 下面注释掉了对 left 和 right 的去重逻辑,因为即使这里没有去重,由于if条件,left 和 right仍然会收缩
// while (left < right && nums[left] == nums[left-1])
// {
// left++;
// }
}
else{
right--;
// while (left < right && nums[right] == nums[right+1])
// {
// right--;
// }
}
}
}
return result;
}
时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
空间复杂度:
O
(
1
)
O(1)
O(1),返回值需要用到的空间不计入额外空间
四数之和代码【C++】代码:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result;
int i,j,left,right;
int n = nums.size();
sort(nums.begin(),nums.end());
for(i = 0; i < n; i++){
if(i > 0 && nums[i] == nums[i-1]) continue;
for(j = i + 1; j < n; j++){
if(j > i + 1 && nums[j] == nums[j-1]) continue;
left = j + 1;
right = n - 1;
while(left < right){
// 4数之和可能会溢出,转化为long
if((long)nums[i] + nums[j] + nums[left] + nums[right] < target) left++;
else if((long)nums[i] + nums[j] + nums[left] + nums[right] > target) right--;
else {
result.push_back(vector<int>{nums[i], nums[j], nums[left++], nums[right--]});
while(left < right && nums[left] == nums[left-1]) left++;
while(left < right && nums[right] == nums[right+1]) right--;
}
}
}
}
return result;
}
时间复杂度:
O
(
n
3
)
O(n^3)
O(n3)
空间复杂度:
O
(
1
)
O(1)
O(1)
参考文档
- https://programmercarl.com/0454.%E5%9B%9B%E6%95%B0%E7%9B%B8%E5%8A%A0II.html
- https://programmercarl.com/0383.%E8%B5%8E%E9%87%91%E4%BF%A1.html
- https://programmercarl.com/0015.%E4%B8%89%E6%95%B0%E4%B9%8B%E5%92%8C.html
- https://programmercarl.com/0018.%E5%9B%9B%E6%95%B0%E4%B9%8B%E5%92%8C.html