一、四数相加
1、力扣题目
题目链接:454. 四数相加 II - 力扣(LeetCode)
2、视频学习
视频链接:学透哈希表,map使用有技巧!LeetCode:454.四数相加II_哔哩哔哩_bilibili
思路解析:该题既需要数组中的值还需要数组中的下标,并且是在数组中找某些元素,所以可以考虑使用map。并且可以重复,最后仅需返回count的值。
该题需要注意的是两两分组,计算第一组所出现的和以及出现的次数,并放入map,遍历第二组时就仅需查找与题目条件所对应需要的值是否在map中出现过即可,次数相加就是最终的结果。
代码如下:
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int,int> map;
int count=0;
for(int i=0;i<nums1.size();i++){
for(int j=0;j<nums2.size();j++){
int key=nums1[i]+nums2[j];//前两个相加
auto iter=map.find(key);
if(iter==map.end()){
map.insert(pair<int,int>(key,1));//未找到则插入
}
else{
iter->second+=1;
}
}
}//获取了第一个和第二个数组的和的值和出现次数
for(int i=0;i<nums3.size();i++){
for(int j=0;j<nums4.size();j++){
int key=0-(nums3[i]+nums4[j]);
auto iter=map.find(key);
if(iter!=map.end()){
count+=iter->second;//对应value相加
}
}
}
return count;
}
};
3、总结
注意该题与后面的三和四题的区分,因为涉及到两个数的计算,所以考虑使用map,且不用考虑重复
二、赎金信
1、力扣题目
2、文章学习
思路解析:其实比较简单,跟上一篇第一题思路类似,建议自己写一下。
代码如下:
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int hash[26]={0};
for(int i=0;i<magazine.size();i++){
hash[magazine[i]-'a']+=1;
}
for(int i=0;i<ransomNote.size();i++){
hash[ransomNote[i]-'a']-=1;
}
for(int i=0;i<sizeof(hash)/sizeof(hash[0]);i++){
if(hash[i]<0)return false;
}
return true;
}
};
三、三数之和
1、力扣题目
2、视频学习
视频链接:梦破碎的地方!| LeetCode:15.三数之和_哔哩哔哩_bilibili
思路解析:该题主要是为了跟上面第一题进行区分,该题最好使用双指针法,因为涉及到去重,hash不太好去重,而且题目要求返回的是值而非下标,所以此时数组顺序是可以打乱的,在排序之后的数组中找值,就会想到使用双指针。
首先遍历一遍数组作为a的值,然后对a进行去重操作,此时去重逻辑是数组是排过序的,相同的值都集中在一圈,因而如果当前a跟上一个a相等则重复。
选定a之后,b和c使用双指针进行寻找,即nums[left] + nums[right] = -a时的left和right,之后对b和c进行去重,此时的去重逻辑就是如果当前的left或者right跟上一个left或者right相等,则移动left或right指针。注意是while而非if,因为可能出现多个相同的值在一起,要判断多次。
注意当指针找到一个符合要求的b和c之后不能使用break切断循环,而应该两个指针一起向内移动一步,因为判断条件时b+c的和,而该和可能有各种b和c组成,所以最终的停止条件应该是不满足left<right,即整个遍历一遍之后退出。
代码如下:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(),nums.end());//排序
if(nums[0]>0)return {};//返回空
vector<vector<int>> result;
for(int i=0;i<nums.size();i++){//选取a
int target=0-nums[i];
int left=i+1,right=nums.size()-1;//双指针
if(i>0&&nums[i]==nums[i-1])continue;//去重a
while(left<right){
int value=nums[left]+nums[right];//需要找的数之和
//未找到则移动指针
if(value<target){left++;}
else if(value>target){right--;}
//找到则判重
else{
result.insert(result.end(),{nums[i],nums[left],nums[right]});
//对b和c的去重,此时b为left,c为right
while(left<right&&nums[left]==nums[left+1]){left++;}
while(left<right&&nums[right]==nums[right-1]){right--;}
left++;
right--;
//注意是移动指针,而不是break,因为判断的是之和,后面可能还会有和相等的情况
}
}
}
return result;
}
};
3、总结
该题最终结果只与值有关,而且需要进行去重,对数组下标无要求,因而考虑双指针
四、四数之和
1、力扣题目
2、视频学习
视频链接:难在去重和剪枝!| LeetCode:18. 四数之和_哔哩哔哩_bilibili
思路解析:该题思路跟上一题思路一致,可以自己写一下,然后我的代码没有进行剪枝,可以自己进行改进。
代码如下:
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
sort(nums.begin(),nums.end());//排序
vector<vector<int>> result;
if(nums.size()<4)return result;//不符合条件的返回空
for(int i=0;i<nums.size();i++){
if(i>0&&nums[i-1]==nums[i])continue;//a去重
for(int j=i+1;j<nums.size();j++){
if(j>i+1&&nums[j]==nums[j-1])continue;//b去重
int left=j+1;
int right=nums.size()-1;
while(left<right){
long sum=(long)nums[i]+nums[j]+nums[left]+nums[right];//注意类型转化
if(sum<target)left++;
else if(sum>target)right--;
else{
result.insert(result.end(),{nums[i],nums[j],nums[left],nums[right]});
while(left<right&&nums[left]==nums[left+1])left++;//c去重
while(left<right&&nums[right]==nums[right-1])right--;//d去重
left++;
right--;
}
}
}
}
return result;
}
};
3、总结
该题与上题思路一致,可以加深印象
五、总结
当题目中涉及在数值中查某元素是否存在,或者满足条件的元素时,考虑使用hash或者双指针。如果是没有涉及到的下标的数组,可以考虑排序后用双指针,如果题目强调了排序的数组,那么基本上就是双指针,其余情况有优先考虑hash。在选择用哈希表是,也要注意不同的题目选用不同的数据结构。