@代码随想录算法训练营第7天 | LeetCode454 四数相加||,383 赎金信,15 三数之和,18 四数之和(四数之和有一些代码细节进行了系统的注释,建议复习的时候查看一下)
454 四数相加||
视频链接:
https://programmercarl.com/0024.%E4%B8%A4%E4%B8%A4%E4%BA%A4%E6%8D%A2%E9%93%BE%E8%A1%A8%E4%B8%AD%E7%9A%84%E8%8A%82%E7%82%B9.html
第一遍读题思考(五分钟内,如果没有思路就写暴力解法思路,暴力解法思路也不清晰就写无
只能想到暴力法n的四次方时间复杂度。
代码随想录解法思路
可以简化到n的平方,用两个二重循环分别计算两对数组的加和的值存储在两个hash map中,这里map中的key存储加和的值,value用来存储这个key出现了多少次。
c++代码具体实现注意事项
map可以像数组用方括号直接访问,但是比数组要方便,可以直接对之前没有添加过的key所对应value进行操作,初始value就是0.
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
std::unordered_map<int, int> sum1;
for(int a:nums1)
for(int b:nums2){
sum1[a+b]++;
}
int count{0};
for(int c:nums3)
for(int d:nums4){
auto iter = sum1.find(0-c-d);
if(iter!=sum1.end()){
count += iter->second;
}
}
return count;
}
};
学习时长
25分钟
383 赎金信
第一遍读题思考(五分钟内,如果没有思路就写暴力解法思路,暴力解法思路也不清晰就写无
用map做哈希表,然后先把magazine里的字母都统计到map中,然后再遍历ransomnote中的字母,找到就减减,然后查看所有key有没有对应的value小于0的。
代码随想录解法思路
不用map,用26大小的数组,因为map操作消耗更多,只有小写字母。
c++代码具体实现注意事项
map可以像数组用方括号直接访问,但是比数组要方便,可以直接对之前没有添加过的key所对应value进行操作,初始value就是0.
注意只用了两个for循环,把原本的检验hash小于0的步骤直接放在第二个循环里了。
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']++;
}
for(int i=0;i<ransomNote.size();i++){
hash[ransomNote[i]-'a']--;
if(hash[ransomNote[i]-'a']<0){
return false;
}
}
return true;
}
};
学习时长
14分钟
15 三数之和
第一遍读题思考(五分钟内,如果没有思路就写暴力解法思路,暴力解法思路也不清晰就写无
双重for循环边遍历边更新hash表,但是这样好像会存在重复的数组。
代码随想录解法思路
非常巧妙,用双指针法。一个基本for循环产生a,然后left指针指向a右边一个数,right指针指像数组最末尾,然后判断a+left+right是否等于0,如果等于0,left和right同时收缩,如果大于0,right收缩,如果小于0,left收缩。如此一来便收集了以a为基底的所有组合。但是记得要对left和right进行去重,left找到以后检查left的右边是否相同,right亦然。同样对基底a也要进行去重,检查a和a-1是否相同,相同直接continue。
最最重要的一点,前面这个方法成立完全基于数组是排好顺序的。
双指针法yyds!!!!!!
c++代码具体实现注意事项
注意去重操作,这里在遍历的时候去重就不需要在结果中去重了。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
std::sort(nums.begin(), nums.end());
for(int i=0;i<nums.size();i++){
if(i==0 && nums[i]>0){
return result;
}
if(i>0 && nums[i]==nums[i-1]){
continue;
}
int left = i+1;
int right = nums.size()-1;
while(right>left){
if(nums[i]+nums[left]+nums[right]<0){
left++;
}
else if(nums[i]+nums[left]+nums[right]>0){
right--;
}
else{
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++;}
right--;
left++;
}
}
}
return result;
}
};
学习时长
40分钟
18 四数之和
第一遍读题思考(五分钟内,如果没有思路就写暴力解法思路,暴力解法思路也不清晰就写无
感觉可以用一个二重循环生成一个map的哈希表,然后再去找,不过感觉去重非常复杂。
代码随想录解法思路
延续三数之和的思想,仍然采用双指针法,只不过基底指针采用了双重循环。要对两次循环进行两级剪枝操作,用于剔除异常数据。
此外最开始应该进行判断如果数组长度小于4曾直接返回。
最后,还需要注意四个数相加可能溢出的情况,需要转换成long类型,查看下面的代码看注释标注的地方。
c++代码具体实现注意事项
class Solution {
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result;
std::sort(nums.begin(), nums.end());
if(nums.size()<4){ // 小于长度4的数组直接进行返回
return result;
}
for(int i=0;i<nums.size()-3;i++)
for(int j=i+1;j<nums.size()-2;j++){ // 两重循环,基底有两个,所以要进行分别剪枝和去重操作
if(nums[i]>target && target>=0){ // 一级剪枝注意这道题里的target可能是负数,所以单纯的大于target是不可以的
break;
}
if(i>0 && nums[i] == nums[i-1]){ // 一级去重操作,新的基底要与之前的基底进行比较
continue;
}
if(nums[i]+nums[j]>target && target>=0){ // 二级剪枝操作注意这里两个基底为一个整体进行判断
break;
}
if(j>i+1 && nums[j] == nums[j-1]){ // 二级去重操作注意,第二个基底是在第一个基底基础上选择的,所以不是大于1!!!
continue;
}
int left = j+1;
int right = nums.size()-1;
while(left<right){
if((long) nums[i]+nums[j]+nums[left]+nums[right]>target){ // 注意int类型的数相加会溢出的问题
right--;
}
else if((long) nums[i]+nums[j]+nums[left]+nums[right]<target){
left++;
}
else{
result.push_back({nums[i],nums[j],nums[left],nums[right]});
while(left<right && nums[left+1]==nums[left]){left++;}
while(left<right && nums[right-1]==nums[right]){right--;}
left++;
right--;
}
}
}
return result;
}
};
学习时长
40分钟