454.四数相加II
没思路。。。不知道怎么用上哈希表
看题解
稀里糊涂写出来了
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
int n=nums1.size();
unordered_map<int,int> myMap;
for(int a:nums1){
for(int b: nums2){
myMap[a+b]++;
}
}
int count=0;
for(int c: nums3){
for(int d: nums4){
if(myMap.count(0-(c+d))!=0){
count+=myMap[0-(c+d)];
}
}
}
return count;
}
};
不知道,感觉还要再总结一下这个题,现在脑子有点混乱,说不会吧,照着题解思路一路下来确实写出来了。说会吧,这个题给我一种二刷不一定能写对的没把握感(谁知道为什么会有这种感觉?)
时间复杂度O(N^2)
383. 赎金信
很容易有思路的一道题,思路就不再赘述。
贴一下我AC的版本
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int len=magazine.size();
unordered_map<char,int> myMap;
for(int i=0;i<len;++i){
myMap[magazine[i]]++;
}
int len2=ransomNote.size();
for(int i=0;i<len2;++i){
if(myMap[ransomNote[i]]==0){
return false;
}
else{
--myMap[ransomNote[i]];
}
}
return true;
}
};
后知后觉发现我的提交时间占比很靠后,虽然已知力扣的那个统计数据不准确,但还是引起了我的注意。
看了一下题解,这里用数组会比哈希表更快,卡哥是这样写的:
“
一些同学可能想,用数组干啥,都用map完事了,其实在本题的情况下,使用map的空间消耗要比数组大一些的,因为map要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效!
”
换数组写一下:
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int len=magazine.size();
int myMap[26]={0};
for(int i=0;i<len;++i){
myMap[magazine[i]-'a']++;
}
int len2=ransomNote.size();
for(int i=0;i<len2;++i){
if(myMap[ransomNote[i]-'a']==0){
return false;
}
else{
--myMap[ransomNote[i]-'a'];
}
}
return true;
}
};
为了减少修改的部分直接把数组的名字叫myMap了
时间一下子快了起来
这里有一个26个字母直接得到数组下标的方法:
magazine[i]-‘a’
15. 三数之和
之前做过。。。隐约记得是没用哈希表的方法,其他的想不起来。
突然发现我之前还真是无效做题啊😭
看题解!
哈希表的题解,j剪枝的逻辑完全没看懂。
写双指针法吧,感叹一句好巧妙的去重逻辑!
思路大体如下:
先数组排序。
第一个指针为i,放在一个for循环从头开始循环,第二个指针取名left,起始位置在i的下一个,第三个指针取名right,起始位置在最大的位置。
求三者目前的和,如果和为正,说明最大的数取大了,right–;如果和为负,说明第二个指针取小了(为什么不是第一个指针?第一个指针是用来遍历整个数组的,是起点位置,相当于对整个数组的每一个数,在他右边所有的数中找出两个与他和为零的,这个过程需要遍历右边所有的可能的组合,所以在遍历结束之前(也就是right<=left之前)是不能改变i的 )(前面这段话有点赘述了,如果能理解为什么先移动第二个指针而不是第一个,就可以忽略不看)
此时移动第二个指针left++,直到能存在有和为0的情况,把这个情况存入result数组。
下面说一下在遍历的时候去重的思路:(真的很精妙)
在第一个指针循环的时候,如果此次循环结束进入下一个循环,发现下一个循环的第一个数与上一个循环第一个数相等,那么直接跳过这个数。也就是nums[i-1]==nums[i]的时候需要continue(此处注意i-1需要>=0)
为什么不是nums[i+1]==nums[i]?
注意第二个指针left是从i+1的位置开始的,如果是这种判断条件,相当于直接排除了所有第一个指针和第二个指针数相同的情况,这样会错过[-1,-1,2]的这种情况。
left和right去重,则是在循环中找到等于0的情况,下一步需要向内同时收缩left和right(此时如果left<right,第一个指针i还是不变的)。收缩的时候如果碰到相同的left或者right需要继续收缩以去重。
好了,上代码:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
int left,right;
int size=nums.size();
vector<vector<int>> result;
if(size<3) return result;
sort(nums.begin(),nums.end());
for(int i=0;i<size;++i){
if(nums[i]>0) break;
if(i>0 &&nums[i]==nums[i-1]) continue;
left=i+1;
right=size-1;
while(right>left){
int num=nums[i]+nums[left]+nums[right];
if(num>0) --right;
else if(num<0) left++;
else{
result.push_back({nums[i],nums[left],nums[right]});
left++;
right--;
while(left<right && nums[left]==nums[left-1]) left++;
while(left<right && nums[right]==nums[right+1]) right--;
}
}
}
return result;
}
};
时间复杂度O(N^2)
感觉是值得隔一段时间就刷几遍的题,防止忘记去重思路。
(哈希表方法有时间再看看 先挖个坑)
18 四数之和
先试着模仿上一道题的思路做了一下:
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result;
int left,right;
int n=nums.size();
sort(nums.begin(),nums.end());
for(int i=0;i<n;++i){
if(nums[i]>target && (target>0 || nums[i]>0)) break;
if(i>0 && nums[i]==nums[i-1]) continue;
for(int j=i+1;j<n;++j){
if(nums[i]+nums[j]>target && nums[i]+nums[j]>=0) break;
if(j>i+1 && nums[j]==nums[j-1]) continue;
left=j+1;
right=n-1;
while(left<right){
long int sum=nums[i];
sum+=nums[j];
sum+=nums[left];
sum+=nums[right];
if(sum>target) right--;
else if(sum<target) left++;
else{
result.push_back({nums[i],nums[j],nums[left],nums[right]});
left++;
right--;
while(left<right && nums[left]==nums[left-1]) left++;
while(left<right && nums[right]==nums[right+1]) right--;
}
}
}
}
return result;
}
};
看了一下题解,思路差不多是一样的,照着题解修改的是对于target的判断部分。
如果nums[i]大于target,不可以直接返回,因为可能存在[-4,-3,-2,-1]这种情况,
只有当nums[i]大于0的时候才可以break。
还有一个小的部分需要注意,计算sum的时候可能会溢出,需要用long int型变量。
而我一开始直接写的:
long int sum = nums[i] + nums[j] + nums[left] + nums[right];
还是会报错,于是我就选择了把四个累计加起来,第一种会报错的理由如下:
(因为gpt解释得很清楚所以我就直接贴上截图了)