Day7 哈希表(二)
2023.12.6
深感抱歉,这几天事情太多了,昨天跟导师出差,整理资料到半夜,一直没空打卡学习,今晚难得有空,先将昨天12.5日的任务补上。
1. 454四数相加Ⅱ
这道题其实还是很好理解的,四个数组的匹配问题,麻瓜一点就是四层for,显然不是我们想要的最优解,尽量减少时间复杂度。延续之前做哈希表题的思想,固定一个值,然后找另一个值。但此处是四个值,因此需要做组合,两两组合或者三一分配。显然两两组合时间复杂度小一点,将四个值变为两个值匹配。分别两层for,将四值之和变为两值之和。
另一个技巧,本题找的是和为0的,我们转换成两个值后,也就是互为相反数,因此代码逻辑比之前的要简单一些。直接看另一个数是否在哈希表中即可。
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int, int> map;
for(int a:nums1){ //二合一
for(int b:nums2){
map[a+b]++;
}
}
int count=0;
for(int c:nums3){ //二合一且查找匹配
for(int d:nums4){
if(map.find(0-(c+d))!=map.end()){
count += map[0-(c+d)];
}
}
}
return count;
}
};
2. 383赎金信
这道题很简单,做了之前的哈希表问题,再看这道题就很easy了,将对照字符串遍历,值++;然后遍历待处理字符串,值–;最后遍历map,如果有值<0的,那就问题啦!
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
unordered_map<int,int> map;
for(char s:magazine){
map[s-'a']++;
}
for(char s:ransomNote){
map[s-'a']--;
}
for(auto i = map.begin();i!=map.end();i++){
if(i->second<0){
return false;
}
}
return true;
}
};
3. 15三数之和
这道题最开始我就想的继续用map,但代码逻辑很乱,一直写不出来,最后看代码随想录,emmm,绷不住了,这种方法果然不是最优解,本题应该使用双指针,即一个for控制遍历,然后定义一个指针指向i+1,另一个指向末尾。不过,前提是数组要先排序!很巧妙,然后三者值相加,判断大于零还是小于零,大于零,那正数就要变小,所以右侧的左移。小于零,负数要变大,因此左侧的要右移。整体逻辑简单。但存在很多细节。
1.如果遍历i的值,nums[i]都是大于0的,那还有必要继续吗?最小值大于零,三者之和肯定不能为0了。因此直接return。
2.题目要求不重复,就要对三个数进行删选,即相邻值相同的略过
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
for(int i=0;i<nums.size();i++){
if(nums[i]>0)
return result;
int left = i +1;
int right = nums.size()-1;
if(i>0 && nums[i]==nums[i-1])
continue;
while(left<right){
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]});
while(left<right && nums[right]==nums[right-1])
right--;
while(left<right && nums[left]==nums[left+1])
left++;
right--;
left++;
}
}
}
return result;
}
};
4. 18四数之和
本题与三数之和相同,但外面多一层循环,并且,这次target是指定值,因此在剪枝操作就不能>0就return了,具体代码(用的代码随想录原始代码,因为在出差期间,时间不够,就偷个懒。。。)如下:
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) {
break; // 这里使用break,统一通过最后的return返回
}
// 对nums[k]去重
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) {
break;
}
// 对nums[i]去重
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 ((long) nums[k] + nums[i] + nums[left] + nums[right] > target) {
right--;
// nums[k] + nums[i] + nums[left] + nums[right] < target 会溢出
} else if ((long) nums[k] + nums[i] + nums[left] + nums[right] < target) {
left++;
} else {
result.push_back(vector<int>{nums[k], nums[i], nums[left], nums[right]});
// 对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;
}
};