代码随想录算法训练营
Day6 代码随想录算法训练营第六天 |LeetCode454.四数相加II LeetCode 383. 赎金信 LeetCode15. 三数之和 LeetCode18. 四数之和
目录
前言
今天后两道题由于去重的复杂性不能使用哈希法
(如果把数组放进map,再用.find判断是否出现,会超时)
LeetCode454.四数相加II
LeetCode383. 赎金信
LeetCode15. 三数之和
LeetCode18. 四数之和
一、LeetCode454.四数相加II
1.题目链接
2.思路
(1)先用两次for循环遍历A和B,求A+B并作为map的键,出现次数作为map的值
(2)再用两层for循环遍历C和D,判断0-C-D是否为map的键,如果是,答案加上对应的值
3.题解
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
int count=0;
int n=nums1.size();
unordered_map<int,int> m;
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
m[nums1[i]+nums2[j]]++;
}
}
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
auto iter=m.find(0-nums3[i]-nums4[j]);
if(iter!=m.end())
count+=iter->second;
}
}
return count;
}
};
二、LeetCode383. 赎金信
1.题目链接
2.思路
(1)思路与LeetCode242有效的字母异位词相似,字母作为哈希表的索引
(2)ransomNote出现字母则对应元素-1,magazine出现对应字母则对应元素+1
(3)如果最后哈希表里有负数,那么说明ransomNote有字母没有在magazine中出现足够多次
3.题解
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int a[26]={0};
for(int i=0;i<ransomNote.size();i++ )
{
a[ransomNote[i]-'a']--;
}
for(int i=0;i<magazine.size();i++)
{
a[magazine[i]-'a']++;
}
for(int i=0;i<26;i++)
{
if(a[i]<0)return 0;
}
return 1;
}
};
三、LeetCode15. 三数之和
1.题目链接
2.思路
(1)为什么不适合哈希法:把符合条件的三元组放进vector中,然后再去重容易超时
(2)数组排序
(3)for循环遍历找答案里最小的元素A,双指针遍历找另外两个元素BC
和大于0则右指针向左移动,和小于0则左指针向右移动,和为0记录答案,左右指针同时移动(若下一个B或者C和现在的一样,就是重复;若下一个B、C和现在不一样,那么应该移动指针)
(4)剪枝:
答案中最小元素大于0,则跳出循环
(5)去重:
1)A去重:
if (k > 0 && (nums[k] == nums[k - 1]))
continue;
如果某个元素和上一个元素相等,则它已经被讨论过了
为什么不用nums[k] == nums[k +1]判定:这种会舍弃所有有重复元素的答案 e.g nums={0,0,0}
2)B去重
while (i < j && nums[i] == nums[i + 1])
i++;
nums[i]已经被记录了,如果下一个还和它一样,那么跳过
3)C去重
while (i < j && nums[j] == nums[j - 1])
j--;
nums[j]已经被记录了,如果下一个还和它一样,那么跳过
3.题解
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> ans = {};
int n = nums.size();
sort(nums.begin(), nums.end());
for (int k = 0; k < n; k++) {
if (k > 0 && (nums[k] == nums[k - 1]))
continue;
if (nums[k] > 0)
break;
int i = k + 1;
int j = n - 1;
while (i < j) {
if (nums[i] + nums[j] + nums[k] < 0)
i++;
else if (nums[i] + nums[j] + nums[k] > 0)
j--;
else {
ans.push_back(vector<int>{nums[k], nums[i], nums[j]});
while (i < j && nums[i] == nums[i + 1])
i++;
while (i < j && nums[j] == nums[j - 1])
j--;
i++;
j--;
}
}
}
return ans;
}
};
四、LeetCode
1.题目链接
2.思路
(1)在三数之和的基础上加一层for循环
(2)剪枝
1)不能直接用nums[i]>target来剪枝,因为元素可能是负数,相加变小
2)A和B剪枝时要确保元素都是正的
A剪枝
if (nums[i] > target && nums[i] > 0)
break;
B剪枝
if (nums[i] + nums[j] > target && nums[i] + nums[j] > 0)
break;
(3)去重方法和三数之和一样
3.题解
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> res;
int n = nums.size();
sort(nums.begin(), nums.end());
for (int i = 0; i < n; i++) {
if (nums[i] > target && nums[i] > 0)
break;
if (i > 0 && nums[i] == nums[i - 1])
continue;
for (int j = i + 1; j < n; j++) {
if (nums[i] + nums[j] > target && nums[i] + nums[j] > 0)
break;
if (j > i + 1 && nums[j] == nums[j - 1])
continue;
int l = j + 1;
int r = n - 1;
while (l < r) {
long s = (long)nums[i] + nums[j] + nums[l] + nums[r];
if (s < target)
l++;
else if (s > target)
r--;
else {
res.push_back({nums[i], nums[j], nums[l], nums[r]});
while (l < r && nums[l] == nums[l + 1])
l++;
while (l < r && nums[r] == nums[r - 1])
r--;
l++;
r--;
}
}
}
}
return res;
}
};
总结
不用考虑重复问题,只考虑是否出现过,用哈希法简单
如果要考虑去重,用双指针更简单