454.四数相加II
题目链接/文章讲解/视频讲解:代码随想录
解题思路
首先我们会想到暴力解法,但由于我们要进行四次循环,时间复杂度为O(n^4),会超时,因此我们要使用一种便捷的方法,我们可以将等式转换为nums1+nums2 = 0-(nums3+nums4),这样我们只需要将四个数组分为两部分,分别用两个循环即可,只需要去哈希map中寻找是否有0-(nums3+nums4) 这个元素就可以了,并且我们要将出现的次数作为value,这样我们找到后,直接让继续器+=value即可
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
int count=0;
unordered_map<int,int> map;
for(int i=0;i<nums1.size();i++)
{
for(int j=0;j<nums2.size();j++)
{
auto item = map.find(nums1[i]+nums2[j]);
if(item!=map.end())
{item->second++;} //去寻找两个数组的和,如果找到则value++
else
map.insert(pair<int,int>(nums1[i]+nums2[j],1)); //没找到就insert进去
}
}
for(int i=0;i<nums3.size();i++)
{
for(int j=0;j<nums4.size();j++)
{
auto item = map.find(0-(nums3[i]+nums4[j])); //再次遍历,如果找到就让count+=value,最后返回count即可
if(item!=map.end())
count+= item->second;
}
}
return count;
}
};
时间复杂度:O(n)
383. 赎金信
题目链接/文章讲解:代码随想录
解题思路
这题我没想用暴力解法,我直接想到的是定义一个哈希数组,因为数据较小,且只有小写字母可以用26个下标就可以存储,这题和字母的同位词那题相似,这里只要让判断的条件变成数组里有小于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']++; //记录magazine中字母出现的次数
for(int i=0;i<ransomNote.size();i++)
hash[ransomNote[i] - 'a' ]--; //让赎金信中的字母对应减减
for(int i=0;i<26;i++)
if(hash[i]<0) return false; //如果出现了小于0的数,那么代表不能满足条件
return true;
}
};
时间复杂度:O(n)
15、三数之和
题目链接/文章讲解/视频讲解:代码随想录
解题思路
这道题要求不能有重复的三元组,因此使用哈希法再进行去重操作是非常麻烦和耗时的,因此我们可以换一种思路,用双指针法,但我们也要进行去重操作,但去重操作要如何进行呢,到底是nums[i]==nums[i+1]时跳过,还是nums[i]==nums[i-1]时跳过,我们可想而知,如果是前者就跳过,那么是三元组中不能有重复元素,而后者跳过的意思就是,当我们i-1这个下标的数已经经历过一次查过结果后,那么他已经遍历了所有a=nums[i-1]时的情况,因此不需要再走一遍流程,直接跳过即可,而b,c的去重操作也是一样的。然后再一个重要的点就是如何找到等于0的元组呢,因此不要求下标,我们可以使用sort来排序后,如果三个数相加>0,则让right左移缩小,如果<0,则让left左移增大,当到0时,我们收获一个结果后,再进行去重操作,否则会漏掉000这种情况
利用代码随想录的图来做一下演示
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; //如果第一个元素已经大于0了,无论如何都找不到符合条件的三元组,那么就直接返回结果
if(i>0&&nums[i]==nums[i-1]) continue; //去重,a作为有过的数已经判断过所有情况了
int left=i+1;
int right=nums.size()-1;
while(right>left) //开始移动双指针来寻找答案,等于时没有意义
{
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]}); //先收获一个结果,再去重,否则会遗漏000这样的情况
while(right>left&&nums[right]==nums[right-1]) right--; //如果和他下一个要遍历的相同,那么就跳过这个数,并确保右指针在左指针右边
while(right>left&&nums[left]==nums[left+1]) left++;
//此时的right和left依旧指向我们找到的结果,因此要回缩指针
right--;
left++;
}
}
}
return result;
}
};
18、四数之和
题目链接/文章讲解/视频讲解:代码随想录
解题思路
这题和三数之和的思路差不多,但是剪枝操作更为复杂一些,因为target可能会出现为负数的情况,因此我们剪枝只能要求target>0,并且nums[k]>0时才可以进行剪枝操作,逻辑变成nums[i] > target && (nums[i] >=0 || target >= 0)
就可以了,其他的思路是一样的,注意i的取值和更新即可
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; //剪枝操作
if(k>0&&nums[k]==nums[k-1]) continue; //去重
for(int i=k+1;i<nums.size();i++)
{
if(nums[k]+nums[i]>target && nums[k]+nums[i]>0) break;//剪枝操作
if(i>k+1 && nums[i]== nums[i-1]) continue; //去重
int left = i+1;
int right = nums.size()-1;
while(right>left)
{
if((long) nums[k]+nums[i]+nums[left] + nums[right] >target) right--; //四数相加可能会溢出
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]});
while(right>left && nums[right] ==nums[right-1]) right--;
while(right>left && nums[left] ==nums[left+1]) left++;
right--;
left++;
}
}
}
}
return result;
}
};
- 时间复杂度: O(n^3)
- 空间复杂度: O(1)
收获
今天的题难得批爆,人做晕了呜呜继续努力