454.四数相加II
解题思路
本题与前一天两数之和的思路差不多,四个单独的数组,两两处理,减少维度。这里用map而不直接用数组是为了减少空间浪费,而我们需要的仅仅是无序集合,因此使用unordered_map。
解法
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int, int> map;
int cnt=0;
for(int a : nums1)
for(int b : nums2)
map[a+b]++;
for(int c : nums3)
for(int d : nums4)
cnt += map[-(c+d)];
return cnt;
}
};
- 时间复杂度: O ( n 2 ) O(n^2) O(n2)
- 空间复杂度: O ( n 2 ) O(n^2) O(n2)
383. 赎金信
解题思路
与前一天的有效字母异位词类似,不同之处在于本题只要求集合是包含关系,不用集合相等,因此在最后判断对应字母的元素是否小于0即可。
解法
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int nums[26] = {0};
int length_A = ransomNote.size();
int length_B = magazine.size();
for(int i=0; i<length_A; i++)
nums[ransomNote[i]-'a']--;
for(int i=0; i<length_B; i++)
nums[magazine[i]-'a']++;
for(int i=0; i<26; i++)
if(nums[i]<0)
return false;
return true;
}
};
- 时间复杂度: O ( n + m ) O(n+m) O(n+m)
- 空间复杂度: O ( 1 ) O(1) O(1)
15. 三数之和
解题思路
本题难点在于处理不重复,一点一点挤出来三重循环之后发现超时了。。。
解法1(超时)
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
int length = nums.size();
if(length<3)
return result;
sort(nums.begin(), nums.end());
for(int i=0; i<length-2; i++ ){
if(nums[i]>0)
break;
if(i>0 && nums[i]==nums[i-1])
continue;
for(int j=i+1; j<length-1; j++){
if(j>i+1 && nums[j]==nums[j-1])
continue;
for(int k=j+1; k<length; k++){
if(k>j+1 && nums[k]==nums[k-1])
continue;
if(nums[i] + nums[j] + nums[k]==0)
result.insert(result.end(), {nums[i], nums[j], nums[k]});
}
}
}
return result;
}
};
- 时间复杂度: O ( n 3 ) O(n^3) O(n3)
- 空间复杂度: O ( n ) O(n) O(n)
解法2
通过哈希表减少了一重循环,能够通过测试,这里很巧妙的是a、b、c中b和c之间的转换,有点反直觉,并不是很直接。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
int length = nums.size();
if(length<3)
return result;
sort(nums.begin(), nums.end());
for(int i=0; i<length-2; i++ ){
if(nums[i]>0)
break;
if(i>0 && nums[i]==nums[i-1])
continue;
unordered_set<int> set; // 一定要放在一重循环内部,否则可能重复
for(int j=i+1; j<length; j++){
// nums[j]可为b也可为c
if(j>i+2 && nums[j]==nums[j-1] && nums[j-1]==nums[j-2])
continue;
int target = -(nums[i]+nums[j]); // b=-(a+c)
if(set.find(target)!=set.end()){
result.push_back({nums[i], target, nums[j]}); // nums[j]为c
set.erase(target);
} else{
set.insert(nums[j]); // nums[j]为b
}
}
}
return result;
}
};
- 时间复杂度: O ( n 2 ) O(n^2) O(n2)
- 空间复杂度: O ( n ) O(n) O(n)
解法3
看了题解文章之后,发现双指针的写法思路非常清晰,相比于哈希方法不同意出错。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
int length = nums.size();
if(length<3)
return result;
sort(nums.begin(), nums.end());
for(int i=0; i<length-2; i++ ){
if(nums[i]>0)
break;
if(i>0 && nums[i]==nums[i-1])
continue;
int left=i+1, right=length-1;
while(right>left){
if(nums[i]+nums[left]+nums[right]==0){
result.push_back({nums[i], nums[left], nums[right]});
// 去重
while(right>left && nums[left]==nums[left+1]) left++;
while(right>left && nums[right]==nums[right-1]) right--;
left++;
right--; // 找到时同时修改left和right
} else if(nums[i]+nums[left]+nums[right]<0){
left++;
} else{
right--;
}
}
}
return result;
}
};
- 时间复杂度: O ( n 2 ) O(n^2) O(n2)
- 空间复杂度: O ( 1 ) O(1) O(1)
18. 四数之和
解题思路
一拿到这道题着手想到的思路和上一道题差不多,可以排序之后先选定一个元素,剩下就变成一个三数之和问题了。这题的难点在于剪枝条件的处理,以及有的测试样例过大导致加和超出了int类型的上限溢出,因此需要转换类型为更大的整型进行处理,这里采用long long。与上一题的另一个不同点是,上一题的target是给定的0,而本题是不确定的,因此剪枝只能在nums[i]为正且大于target时进行,错误的剪枝条件会导致漏解。
解法
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result;
int length = nums.size();
if(length<4)
return result;
sort(nums.begin(), nums.end());
for(int i=0; i<length-3; i++){
if(nums[i] > target && nums[i]>0)
break;
if(i>0 && nums[i]==nums[i-1])
continue;
for(int j=i+1; j<length-2; j++){
if(nums[j] > target-nums[i] && nums[j]>0)
break;
if(j>i+1 && nums[j]==nums[j-1])
continue;
int left=j+1, right=length-1;
while(left<right){
long long a=nums[i], b=nums[j], c=nums[left], d=nums[right];
if(a+b+c+d<target)
left++;
else if(a+b+c+d>target)
right--;
else{
result.push_back({nums[i], nums[j], nums[left], nums[right]});
while(right>left && nums[left]==nums[left+1]) left++;
while(right>left && nums[right]==nums[right-1]) right--;
left++;
right--;
}
}
}
}
return result;
}
};
- 时间复杂度: O ( n 3 ) O(n^3) O(n3)
- 空间复杂度: O ( 1 ) O(1) O(1)
今日总结
今天的题目学到了比较不一样的双指针以及剪枝的应用,对于这些遥远的名词有了更清晰的认知。

2000

被折叠的 条评论
为什么被折叠?



