第454题.四数相加II
题目链接:https://leetcode.cn/problems/4sum-ii/
看到这题的时候,虽说知道要用哈希,但是却无从下手…,看完卡哥的思路分析之后,写出的代码如下(本题的暴力是O(n4),通过哈希改善之后是O(n2)):
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int,int> map;
int sum1=0,sum2=0,count=0;
for(int i=0;i<nums1.size();i++)
{
for(int j=0;j<nums2.size();j++)
{
sum1=nums1[i]+nums2[j];
if(map.find(sum1)==map.end())
map.insert(pair<int,int>(sum1,1));
else
map.find(sum1)->second++;
}
}
for(int i=0;i<nums3.size();i++)
{
for(int j=0;j<nums4.size();j++)
{
sum2=nums3[i]+nums4[j];
if(map.find(-sum2)!=map.end())
{
count+=map.find(-sum2)->second;
}
}
}
return count;
}
tips:一眼没有思路时,可以先想一想题目的暴力解法,想出来之后在对它的时间复杂度进行改善。还有不要轻易被题目带偏,按自己的理解和思路来。
如本题要的时四元组的个数,我们未必就要真的直接去求四元组,真的把i、j、k、l各自求出来,我们只需要抓住问题的本质就可——这样的组合的个数!想办法把组合的个数求出来就可以了!
总体思路:减少数组个数:把1、2看成一个整体并存入map,把3、4看成一个整体并放到map中来检验是否存在。注意,当存在sum1=-sum2时,四元组的个数就是map中对应的value
无重复元素(想去重)用unordered_set , 允许重复元素存在,用multiset,想要精确的知道元素重复的数量,用unordered_map , key存储元素,value存储元素的数量。
383. 赎金信
题目链接:https://leetcode.cn/problems/ransom-note/description/
这道题比较简单,相当于是242的一个拓展,思路与242相同。具体代码如下:
bool canConstruct(string ransomNote, string magazine) {
int hash[26]={0};
for(int i=0;i<ransomNote.size();i++)
{
hash[ransomNote[i]-'a']++;
}
for(int i=0;i<magazine.size();i++)
{
hash[magazine[i]-'a']--;
}
for(int i=0;i<26;i++)
{
if(hash[i]>0)
return false;
}
return true;
}
15. 三数之和
题目链接:https://leetcode.cn/problems/3sum/description/
双指针法
第一眼看到题目,感觉跟454很想,于是还想继续用哈希法,结果发现“下标互不相等”这个条件满足不了。看完了卡哥的视频之后,用双指针法实现的代码如下:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(),nums.end());
vector<vector<int>> result;
int left=0,right=0;
for(int i=0;i<nums.size();i++)
{
if(nums[i]>0)
return result;
if(i>0&&nums[i]==nums[i-1])
continue;
left=i+1;
right=nums.size()-1;
while(left<right)
{
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]});
while(left<right&&nums[left]==nums[left+1])
left++;
while(left<right&&nums[right]==nums[right-1])
right--;
left++;
right--;
}
}
}
return result;
}
补充知识(STL):
sort(nums.begin(),nums.end()); 将始末位置输入sort()函数中,即可对vector内的元素进行排序(升序)
result.push_back(vector{nums[i],nums[left],nums[right]}); 用push_back来增加或插入元素于vector中
二维数组的格式:vector<vector> result;
插入的格式:push_back(vector{nums[i],nums[left],nums[right]})
总体思路:因为只需要元素的大小,不需要其下标,所以首先对数组进行排序(升序)。然后,用i指针来循环遍历,再设置left和right两个指针,分别指向i之后的序列的首尾两端。其次,由于升序排列,当nums[i]+nums[left]+nums[right]**>**0时,right指针左移;left指针同理。最后,当等于0的时候,将此时的3个元素以数组的形式存在二维数组中。
==思路有点复杂,但更易错的却是去重操作:
- 对i指针去重:当nums[i]==nums[i-1]时要跳过当前i。注意!不能是nums[i]=nums[i+1]!因为i+1在i的后面,left指针可能会指向i+1,这样就会遗漏!
- 对left、right指针去重(只有当nums[i]+nums[left]+nums[right]=0时才需要讨论):以left为例,while(left<right&&nums[left]==nums[left+1]),当left当前位置的值与下一个位置相等时,left要多跳一个位置,由此循环(我错就是错在用if,而不是用while)。
18. 四数之和
题目链接:https://leetcode.cn/problems/4sum/
双指针法
看视频之前我估计与三数之和比较类似,可能需要在前面再来一个for循环。结果跟我想的差不多,后面的left、right指针的操作基本相同,但是前面两个for循环的剪枝、去重的细节还时出乎我的意料的,具体代码如下:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result;
sort(nums.begin(),nums.end());
int left=0,right=0;
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;
left=i+1;
right=nums.size()-1;
while(left<right)
{
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(nums[right]==nums[right-1]&&left<right)
right--;
while(nums[left]==nums[left+1]&&left<right)
left++;
right--;
left++;
}
}
}
}
return result;
}
剪枝、去重:
- 对 k指针:
- 剪枝:if(nums[k]>target&&nums[k]>=0),此时后面就不需在讨论了,一定大于target。要注意的是:nums[k]是正数时才能满足条件,因为正正相加越来越大,负负相加越来越小
- 去重:if(k>0&&nums[k]==nums[k-1])
- 对 i指针:
- 剪枝:(nums[k]+nums[i]>target&&nums[k]+nums[i]>=0,原理与k的类似,但是这是要将nums[k]和nums[i]同时算进去。
- 去重:if(i>k+1&&nums[i]==nums[i-1]),要注意的时 i 是在 k 之后的,所以 i 要大于 k+1
另外,题目中nums[i]可以取到10亿,而int最大可以取到21亿,当4个nums[i]相加时,是有可能溢出的,所以要将int转换位long