454.四数相加||
- 暴力法。四层for循环。时间复杂度为O(n4)
- 哈希表法。每两个数组为一组,将每个相对位置相加的结果存储在unordered_map中。最后用一层for循环寻找满足条件的四个数字。时间复杂度为O(n2)
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
//ma1,ma2用来记录和数值以及对应的次数
unordered_map<int,int> ma1,ma2;
for(int i=0;i<nums1.size();++i){
for(int j=0;j<nums2.size();++j){
++ma1[nums1[i]+nums2[j]];
}
}
for(int i=0;i<nums3.size();++i){
for(int j=0;j<nums4.size();++j){
++ma2[nums3[i]+nums4[j]];
}
}
int ans = 0 ;
for(auto it = ma1.begin();it!=ma1.end();++it) {
ans += it->second * ma2[-it->first];
}
return ans;
}
};
383.赎金信
- 将magazine中出现的字符以及对应出现的次数记录在哈希表中。将ransomNote中出现的字符也记录在哈希表中。然后两个哈希表逐一比较。虽然时间复杂度控制在O(n)。但是做了三次循环。其实可以将其中一个循环优化掉。之将magazine的字符记录在哈希表中。详解请见代码。
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int ham[26] = {0};
for(const char &c:magazine) ++ham[c-'a'];
for(const char &c:ransomNote){
if(--ham[c-'a'] < 0) return false;
}
return true;
}
};
15.三数之和
- 暴力解法。三层循环。
- 由两数之和得到的启示,可以利用哈希表进行优化。变成两层for循环。但是需要先用set去重,之后将去重后的答案变成vector返回。做了很多不必要的操作。
- 用双指针进行优化。时间复杂度为O(n2)
双指针
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> ans;
sort(nums.begin(),nums.end());
for(int i=0;i<nums.size();++i){
if(nums[i]>0) return ans;
//去重
if(i>0 && nums[i-1] == nums[i]) continue;
int val = -nums[i];
int l=i+1,r=nums.size()-1;
while(l<r){
if(nums[l]+nums[r]==val){
ans.emplace_back(vector<int>{nums[i],nums[l],nums[r]});
//去重
while(l<r&&nums[l+1]==nums[l]) ++l;
while(l<r&&nums[r-1]==nums[r]) --r;
++l,--r;//向前走
}else if(nums[l]+nums[r]>val) --r;
else ++l;
}
}
return ans;
}
};
排序是为了更好的去重
18.四数之和
双指针
时间复杂度为O(n3)
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> ans;
sort(nums.begin(),nums.end());
for(int i=0;i<nums.size();++i){
if(nums[i]>target&&nums[i]>=0) break;
if(i>0&&nums[i-1]==nums[i]) continue;
for(int j=i+1;j<nums.size();++j){
if(nums[i]+nums[j]>target && nums[i]+nums[j]>=0) break;
if(j>i+1&&nums[j-1]==nums[j]) continue;
long val = (long)target-nums[i]-nums[j];
int l=j+1,r=nums.size()-1;
while(l<r){
if((long)nums[l]+nums[r]==val){
ans.emplace_back(vector<int>{nums[i],nums[j],nums[l],nums[r]});
while(l<r&&nums[l+1]==nums[l]) ++l;
while(l<r&&nums[r-1]==nums[r]) --r;
++l,--r;
}else if((long)nums[l]+nums[r]>val) --r;
else ++l;
}
}
}
return ans;
}
};
需要注意的地方:
- 第一层循环剪枝:if(nums[i]>target&&nums[i]>=0)。第一次编写的时候直接写成了if(nums[i]>target)这样写是不对的,因为此时不知道nums[i]与0的关系,如果nums[i]<0那么target加上nums[i]之后的元素可能会变小,所以还需要判断nums[i]与0的关系。二层内循环的剪枝也是如此。
- 对于本题的数据相加或者相减可能会产生溢出。所以需要将数据强制类型转换为数据范围更大的long类型。
- 内存循环剪枝的时候“if(nums[i]+nums[j]>target && nums[i]+nums[j]>=0) break;”应该使用break退出当前的for循环。而不是return推出整个循环直接结束程序。