Day7 哈希表
今天是哈希表的第二天,又会有什么样的题在前面等着呢
LeetCode 454.四数相加II【哈希表】
你先来两数相加,现在四数相加了,而且四重暴力循环必定超时,怎么办呢?那就把四数分成两个两数不就行了,两个n2相加,复杂度还是O(n2)。
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int,int> umap;
for(int a:nums1)
for(int b:nums2)
umap[a+b]++;
int cnt=0;
for(int c:nums3)
for(int d:nums4){
if(umap.find(0-(c+d))!=umap.end())
cnt+=umap[0-(c+d)];
}
return cnt;
}
};
LeetCode 383.赎金信【哈希表】
题目背景:以前绑架犯为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思。最后贴成的信件,大概就像P5里的预告信一样?
回到题目本身,这道题几乎和字母异位词完全一致,区别在于两个字符串长度无法作为判断标准,而且只需要单向满足条件即可,所以我们就先将待拼凑的magazine数组的字符情况存入,再用另一个减即可。
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int rec[26]={0};
for(int i=0;i<magazine.length();i++)
rec[magazine[i]-'a']++;
for(int i=0;i<ransomNote.length();i++){
rec[ransomNote[i]-'a']--;
if(rec[ransomNote[i]-'a']<0)
return false;
}
return true;
}
};
LeetCode 15.三数之和【双指针】
一刻也来不及为做出二数与四数之和而高兴,立马赶到战场的是——三数之和!
令人兴奋不已的三个数的和,我们的想法自然是把三数拆成2+1,先枚举第一个数,然后遍历后两个数,让后两个数之和满足target-第一个数。
多点同时遍历,首先想到的自然是双指针算法,本题用双指针的效率也很高,注释直接写到代码里了,注意结束的条件是后两个遍历指针从两边在中间重合了。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
int n=nums.size();
vector<vector<int>> res;
sort(nums.begin(),nums.end());
//枚举a
for(int a=0;a<n;a++){
//与a相同的数直接跳过
if(a>0 && nums[a]==nums[a-1]) continue;
//c对应的指针初始从数组最右端开始
int c=n-1;
int target=-nums[a];//转化为两数之和,即后两数之和=第一个数的相反数
//枚举b
for(int b=a+1;b<n;b++){
//与b相同的数直接跳过
if(b>a+1 && nums[b]==nums[b-1]) continue;
//需要保证b的指针在c指针的左侧
while(b<c && nums[b]+nums[c]>target) c--;
//指针重合后,b再增加,就不会有满足b<c且和满足条件的c了,退出循环
if(b==c) break;
if(nums[b]+nums[c]==target)
res.push_back({nums[a],nums[b],nums[c]});
}
}
return res;
}
};
LeetCode 18.四数之和【哈希表】
在三数之和的基础上多加了一层循环遍历,同时要注意剪枝的条件。
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result;
sort(nums.begin(),nums.end());
for (int k=0;k<nums.size();k++) {
// 剪枝处理
if (nums[k]>target && (nums[k]>= 0||target>=0)){
break; // 这里使用break,统一通过最后的return返回
}
// 去重
if (k>0 && nums[k]==nums[k-1]){
continue;
}
for (int i=k+1;i<nums.size();i++){
// 2级剪枝处理
if (nums[k]+nums[i]>target && (nums[k]+nums[i]>= 0 || target>=0)) {
break;
}
// 正确去重方法
if (i>k+1 && nums[i]==nums[i-1]){
continue;
}
int left=i+1;
int right=nums.size()-1;
while (right>left){
// nums[k]+nums[i]+nums[left]+nums[right]>target 会溢出
if (nums[k]+nums[i]>target-(nums[left]+nums[right])){
right--;
// 当前元素不合适了,可以去重
while (left<right && nums[right]==nums[right+1]) right--;
// nums[k] + nums[i] + nums[left] + nums[right] < target 会溢出
} else if (nums[k]+nums[i]<target-(nums[left]+nums[right])){
left++;
// 不合适,去重
while (left<right && nums[left]==nums[left-1]) left++;
} else {
result.push_back(vector<int>{nums[k],nums[i],nums[left],nums[right]});
// 去重逻辑应该放在找到一个四元组之后
while (right>left && nums[right]==nums[right-1]) right--;
while (right>left && nums[left]==nums[left + 1]) left++;
// 找到答案时,双指针同时收缩
right--;
left++;
}
}
}
}
return result;
}
};
今天的题目做着好累,加完这个加那个,好好休息一下。