第454题.四数相加II
和两数之和很相似,直接把四个数组分为两个大组。A+B的每一种可能以及出现的次数放进map,再去找 0-(c+d),每找到一次count计数,要加上0-(c+d)这个k对应的value值,而不是++。
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int,int> umap;
int count = 0;
for(int a : nums1){
for(int b : nums2){
umap[a+b]++;
}
}
for(int c : nums3){
for(int d : nums4){
auto it = umap.find(0 - (c + d));
if(it != umap.end()) count += umap[0 - (c + d)];
}
}
return count;
}
};
383. 赎金信
这道题和 242.有效的字母异位词 非常像。几乎就是一模一样
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
if(magazine.size() < ransomNote.size()) return false;
int set[26] = {0};
for(char c : ransomNote){
set[c - 'a']++;
}
for(char c : magazine){
set[c - 'a']--;
}
for(int a : set){
if(a > 0) return false;
}
return true;
}
};
第15题. 三数之和(很不熟练 多写几遍)
- 明确题意:题目不仅要求,一组的三元素下标不能相同,并且!两组不能重复!
四数之和II,并不要求数组中的元素不能被使用两次,所以可以直接分组,然后用find(target-(c+d)) 的方式来做。但是三数之要求:数组中的元素,在每一组符合条件的答案中只能出现一次。两数之和只会出现一个答案,并且先查询再遍历的方式可以确保元素只被使用一次。但是三数之和如果使用find(target-(a+b))的方式,去重会变得异常麻烦。所以考虑使用双指针。
先排序,一次for循环,right指向数组尾端,left指向 i+1。 - 去重方法:
下标的去重,保证一组答案里没有被重复使用的元素非常容易,使用双指针和正确的边界条件就可以做到。本题的难点在于:每一组答案都互不重复。
例如:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。
以下为具体的去重方法- [ 1 ] 首先是a的去重:
虽然已经排序过了,但是要考虑{-1,-1,-1,2}这种情况,如果a的去重上来就让index = 2,就会漏掉 -1 -1 2这一组答案。三个相同的数相加等于0的情况只有000,所以最多保留两个相同的数即可。即使前两个数是0 0 ,left = i+1 nums[left] = 0的时候 三个0的情况就被考虑进来了
- [ 1 ] 首先是a的去重:
// 错误去重a方法,将会漏掉-1,-1,2 这种情况
if (nums[i] == nums[i + 1]) {
continue;
}
- [ 2 ] b c的去重:
如果去重放在while一进来的地方:
// 去重复逻辑如果放在这里,0,0,0 的情况,可能直接导致 right<=left 了,从而漏掉了 0,0,0 这种三元组
/*
while (right > left && nums[right] == nums[right - 1]) right–;
while (right > left && nums[left] == nums[left + 1]) left++;
*/
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(),nums.end());
vector<vector<int>> vec;
for(int i = 0; i < nums.size(); i++){
//如果排序后第一个元素就大于0
if(nums[i] > 0) return vec;
//对a去重
//不满足这个条件就直接到left那一步,满足条件就i++
//加一个i>0是为了排除 i=0 这个情况出现nums[-1]
//这样保证几个重读元素是从头被遍历的 不漏掉{-1 -1 2}这种情况
if(i > 0 && nums[i] == nums[i - 1]) continue;
int left = i + 1;
int right = nums.size()-1;
while(left < right){
if(nums[left] + nums[right] + nums[i] > 0) right--;
else if(nums[left] + nums[right] + nums[i] < 0) left++;
else if(nums[left] + nums[right] + nums[i] == 0) {
vec.push_back({nums[left] , nums[right] , nums[i]});
//找到以后才需要去重
while(right > left && nums[left] == nums[left+1]) left++;
while(right > left && nums[right] == nums[right-1]) right--;
//此时的left和right是当前的答案 上面只是做了去重,
//比如{3,3,2,4,5,5},只是从index 0 5到了1 4 而不是2 3
//所以需要向内收缩
left++;
right--;
}
}
}
return vec;
}
};
自己写的暴力解法,三次for循环,时间复杂度是O(n3)
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> res;
sort(nums.begin(), nums.end());
for (int i = 0; i < nums.size(); i++) {
if (nums[i] > 0) return res;
while (i > 0 && i < nums.size()&& nums[i - 1] == nums[i]) i++;
for (int j = i + 1; j < nums.size(); j++) {
if (nums[i] + nums[j] > 0) break;
while (j > i + 1 && j < nums.size() && nums[j - 1] == nums[j])j++;
for (int k = j + 1; k < nums.size(); ) {
if (nums[i] + nums[j] + nums[k] < 0) k++;
if (nums[i] + nums[j] + nums[k] > 0) break;
if (nums[i] + nums[j] + nums[k] == 0) {
res.push_back({ nums[i],nums[j],nums[k] });
k++;
}
while (k > j + 1 && k < nums.size() && nums[k - 1] == nums[k]) k++;
}
}
}
}
};
第18题. 四数之和
跟上一题三数之和相比,就是多了一层for循环。其他去重思路基本一致,和三数之和一样,将时间复杂度降低了n,将原本暴力O(n4)的解法,降为O(n3)的解法
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
sort(nums.begin(),nums.end());
vector<vector<int>> result;
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] == nums[j-1]) continue;
int left = j+1;
int right = nums.size() - 1;
while(left < right){
if(left < right && (long)nums[i] + nums[j] + nums[left] + nums[right] > target) right--;
else if(left < right && (long)nums[i] + nums[j] + nums[left] + nums[right] < target) left++;
else {
result.push_back({nums[i] , nums[j] , 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;
}
};