454.四数相加II
这道题看到第一时间的想法有些模糊,仔细阅读题目,发现不需要把具体的索引返回,所以考虑可以使用哈希表来解决,把第一个和第二个数组的和存到map中,同时把次数也统计到;然后在把另外两个数组各个索引求和sum,查找-sum是否在map中即可,这样相比暴力求解法,时间复杂度从O()降低为O(
),详细代码如下:
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int,int> map1;
int result = 0;
for(int i=0;i<nums1.size();i++)
{
for(int j=0;j<nums2.size();j++)
{
;
int tmp = nums1[i]+nums2[j];
if(map1.find(tmp)!=map1.end()) map1[tmp]++;
else map1[tmp]=1;
}
}
for(int i=0;i<nums3.size();i++)
{
for(int j=0;j<nums4.size();j++)
{
int tmp = -(nums3[i]+nums4[j]);
if(map1.find(tmp)!=map1.end()) result+=map1[tmp];
}
}
return result;
}
};
在做这道题目的时候遇到了leetcode的一个错误:
==20==ERROR: AddressSanitizer: heap-buffer-overflow on address xxx
查资料发现这个bug的原因是数组越界,最终排查到为写双层嵌套循环时候j<num2.size()这一条件写成了i<num2.size(),后续如果再遇到这种错误可以考虑边界条件写错的可能性。
在看完代码随想录后,还学到了一个map的语法知识,即当map中没有key值时不用特殊处理,直接++即可
for(int i=0;i<nums1.size();i++)
{
for(int j=0;j<nums2.size();j++)
{
;
int tmp = nums1[i]+nums2[j];
map1[tmp]++;
}
}
383. 赎金信
这道题目和异位词有相似之处,不同的只需判断A是否由B组成即可,把B做成哈希表,然后遍历A的字符查找是否在B中有即可。
此外学习了C++ 11语法知识之for循环:
详细代码如下:
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
unordered_map <char, int> map1;
for(char s:magazine)
{
map1[s]++;
}
for(char s:ransomNote)
{
if(map1.find(s)==map1.end()||map1[s]<=0) return false;
map1[s]--;
}
return true;
}
};
代码随想录建议用数组来当字典更加有效,原因是:其实在本题的情况下,使用map的空间消耗要比数组大一些的,因为map要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效!
15. 三数之和
这道题目十分经典,使用双指针来减少一层循环,使得三数之和复杂度变为O(),主要有几个去重逻辑需要重点看,我在这道题目中犯了一个错误是:我考虑到找到满足条件的结果需要分别去重左边界和有边界,但是我却忘记了在此之外,左右边界要同时收缩去找下一个满足条件的元组,一开始我忘记同时收缩i和j导致进入了死循环,超时。看了代码随想录知道问题所在后修改进行提交,AC题目:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> res;
sort(nums.begin(), nums.end());
for(int k=0;k<nums.size()-2;k++)
{
if(nums[k]>0) break;
if(k>0&&nums[k]==nums[k-1]) continue;
int i=k+1, j=nums.size()-1;
while(i<j)
{
if(nums[k]+nums[i]+nums[j]==0)
{
res.push_back({nums[k],nums[i], nums[j]});
while(i+1<j&&nums[i+1]==nums[i]) i++;
while(j-1>i&&nums[j-1]==nums[j]) j--;
//找到答案要同时收缩,不然就陷入死循环了
i++;
j--;
}
else if(nums[k]+nums[i]+nums[j]<0) i++;
else j--;
}
}
return res;
}
};
18. 四数之和
这道题目和三数之和类似,但是这道题需要注意的点有两个:
1. 因为target变成了随机数而不是零,所以不可以用nums[k]>target的逻辑去剪枝,如target为负数的话,和比任意一个数更小;在这种情况下,要剪枝必须得保证target>=0,所以两个剪枝可以写为:
if (nums[m]>target&&target>=0)
if (nums[k] + nums[m] > target && target>=0)
2. 这道题需要考虑边界问题,当每个数都很大的时候,四个数相加会大于int的最大值,此时需要将数字强转成long型在进行求和,否则会越界,后续遇到类似的边界也需要考虑该问题。
详细代码如下:
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> res;
sort(nums.begin(),nums.end());
if(nums.size()<4) return res;
for(int m=0;m<nums.size()-3;m++)
{
//if(nums[m]>target) break;
if(m>0&&nums[m-1]==nums[m]) continue;
for(int k=m+1;k<nums.size()-2;k++)
{
//if((nums[m]+nums[k])>target) break;
if(k>m+1&&nums[k]==nums[k-1]) continue;
int i=k+1,j=nums.size()-1;
while(i<j)
{
//此处要主要数据溢出问题,先转换类型
long tmp = (long)nums[m]+nums[k]+nums[i]+nums[j];
if(tmp<target) i++;
else if(tmp>target) j--;
else
{
res.push_back({nums[m],nums[k],nums[i],nums[j]});
while(i+1<j&&nums[i+1]==nums[i]) i++;
while(j-1>i&&nums[j-1]==nums[j]) j--;
i++;
j--;
}
}
}
}
return res;
}
};
今日学习两个小时~