1-文章和视频链接
1.1 454.四数相加II-代码随想录
1.2 454.四数相加II-卡哥B站视频讲解
1.3 383.赎金信-代码随想录
1.4 15.三数之和-代码随想录
1.5 15.三数之和-卡哥B站视频讲解
1.6 18.四数之和-代码随想录
1.7 18.四数之和-卡哥B站视频讲解
2-题目
第一题:454.四数相加II
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int, int> map; // 定义一个unordered map
// key是取nums1和nums2中各一个值的两值之和
// value是此和出现的次数
for(int a:nums1){ // 遍历数组1
for(int b:nums2){ // 对于数组1中每个数,遍历数组2
map[a+b]++; // map中出现的和的次数统计
}
}
int count = 0; // 定义一个计数器,存放元组个数,最后返回的值
for(int c:nums3){ // 遍历数组3
for(int d:nums4){ // 对于数组3中每个数,遍历数组4
if(map.find(0-(c+d))!=map.end()){ // 如果满足(a+b+c+d)=0的key
count += map[0-(c+d)]; // 计数器加上符合条件的value值
}
}
}
return count; // 返回计数器
}
};
- 思路: 原本是暴力解法四个循环嵌套,看了卡哥的视频,2个平行的2个嵌套循环。因为我们需要最后返回元组个数,即出现次数,所以定义一个map,key值为数组1和数组2中各取1个元素相加的和的集合,value则为该和出现的次数;然后构建map,用2个循环嵌套计算数组1和数组2中两两元素相加和及他们的和出现次数;再用2个循环嵌套查找满足数组1-4中各取一数相加和为0的key,这里的key用0-数组3元素-数组4元素=数组1元素和数组2元素的和,即刚刚构建map中的key值;找到的话,计数器加上之前满足条件的key的value值。
- 实现难点: 计数器不是累乘而是累加,计数器并不是+1,而是加上满足条件的key的value值。
- 收获: 理解了map的用法。
第二题:383.赎金信
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int record[26] = {0}; // 定义记录数组,每个元素对应字母位置
// 如果赎金信中字符串长度超过了杂志中字符串长度,杂志肯定无法构成赎金信
if(ransomNote.length()>magazine.length()) return false;
for(int i=0; i<magazine.length(); i++){ // 遍历杂志中每个字母
record[magazine[i]-'a']++; // 杂志中的字母,记录数组中对应位置+1
}
for(int j=0; j<ransomNote.length(); j++){ // 遍历赎金信中每个字母
record[ransomNote[j]-'a']--; // 赎金信中的字母,记录数组中对应位置-1
// 如果记录数组中赎金信对应位置上的字母个数为负
// 说明赎金信中有杂志中没有的字母,返回假
if(record[ransomNote[j]-'a']<0) return false;
}
return true; // 到达此处,返回真
}
};
- 思路: 暴力解法两个循环嵌套擦除后判断长度是否为0;哈希解法中定义一个数组,存放magazine中每个字母的对应位置,然后用ransomNote去判断所有字母是否全部包括。
- 实现难点: 别忘记先判断赎金信与杂志中字符串长度(虽然无此判断,力扣也能过)。
- 收获: 哈希数组在26个字母上很好用。
第三题:15.三数之和
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result; // 定义一个结果数组,可接收多个三元组
sort(nums.begin(), nums.end()); // 双指针法,先对nums数组排序
for(int i=0; i<nums.size(); i++){ // 遍历nums数组
if(nums[i]>0) return result; // 如果第一个数超过0,之后不可能产生三数相加为0
if(i>0 && nums[i]==nums[i-1]) continue; // 第一个数去重,先得到一个结果再去
int left = i + 1; // 定义左指针
int right = nums.size() - 1; // 定义右指针
while(right>left){ // 指针移动的循环,直到左右指针相遇
// 三数相加>0,右指针缩减后退
if(nums[i]+nums[left]+nums[right]>0) right--;
// 三数相加>0,左指针往前
else if(nums[i]+nums[left]+nums[right]<0) left++;
else{ // 三数相加=0,满足条件
// 将三元组存入结果数组中
result.push_back(vector<int>{nums[i], nums[left], nums[right]});
// 第二和第三个数的去重,一定要先得到一个结果再去重
while(right>left && nums[right]==nums[right-1]) right--;
while(right>left && nums[left]==nums[left+1]) left++;
// 存入数组后,2个指针都向对方一步
right--;
left++;
}
}
}
return result; // 返回结果数组
}
};
- 思路: 一开始用哈希法没想出来,后来看了双指针解法,茅塞顿开。遍历数组并定义左右指针。如果三数之和大于0,说明右指针指向的数太大,往后退去试试;如果三数之和小于0,说明左指针指向的数太小,向前移再试试;如果三数之和等于0,就是要找的三元组。
- 实现难点: 关键在于三元组内第一第二第三个元素如何去重以及去重的时机。第一个元素的第一次尝试一定要先经过一遍,不然容易漏解,所以判断去重的条件不应该是nums[i]==nums[i+1]而是i>0 && nums[i]==nums[i-1]。第二第三个元素的去重,在双指针的循环中,也是要得到一个三元组的结果后再去重。
- 收获: 一定要先得到一个结果,再去重;先去重,再存入。
第四题:18.四数之和
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result; // 定义结果数组,存放四元组
sort(nums.begin(), nums.end()); // nums数组排序
for(int i=0; i<nums.size(); i++){ // 四元组第一个元素遍历
if(nums[i]>target && nums[i]>=0) break; // 元素为正数,且超过目标值,后面都不符合条件
if(i>0 && nums[i]==nums[i-1]) continue; // 第一元素查重
for(int j=i+1; j<nums.size(); j++){ // 第二个元素遍历
if(nums[i]+nums[j]>target && nums[i]+nums[j]>=0) break; // 第一第二相加为正数且不超过
if(j>i+1 && nums[j]==nums[j-1]) continue; // 第二个元素查重
// 固定第一第二元素相加,实行双指针,其余和三数之和题目一样
int left = j + 1;
int right = nums.size() - 1;
while(right>left){
// 转换成long为了不溢出
if((long)nums[i]+nums[j]+nums[left]+nums[right]>target) right--;
else if((long)nums[i]+nums[j]+nums[left]+nums[right]<target) left++;
else{
// 将四元素结果保存至结果数组中
result.push_back(vector<int>{nums[i], nums[j], 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; // 返回结果数组
}
};
- 思路: 在三数相加题目基础上,在最外层再嵌套一个循环,固定前2个元素之和,然后再移动双指针即可。
- 实现难点: 如果仅判断固定值和target大小是会漏解,举例-4>-10,后面负数相加也能与target相等,所以还要加上固定值是正数的判断条件。
- 收获: 溢出时格式转换。