454. 四数相加 II - 力扣(LeetCode)
给你四个整数数组
nums1、nums2、nums3和nums4,数组长度都是n,请你计算有多少个元组(i, j, k, l)能满足:
0 <= i, j, k, l < nnums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
暴力法是四层for循环,时间复杂度上为,显然不是最优
如果一次处理两个数组,则时间复杂度为,更优
Step1: 将nums1、nums2的元素和作为键,出现次数作为值,存储为map结构
Step2: 两层for循环遍历nums3、nums4,在map中寻找有没有符合题意的键值对
Step3: count+=absum[-i-j];考虑出现次数!
class Solution {
public:
unordered_map<int,int> absum;
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
for(int i:nums1){
for(int j:nums2){
//两数组某元素之和:i+j作为键
absum[i+j]++; //如果没有i+j这个键,该语句就会执行insert
}
}
int count = 0;
for(int i:nums3){
for(int j:nums4){
count+=absum[-i-j];//3、4元素和为i+j,若map里有-i-j,则满足题意
//注意,有多种出现-i-j的可能,都可计入结果
}
}
return count;
}
};
383. 赎金信 - 力扣(LeetCode)
给你两个字符串:
ransomNote和magazine,判断ransomNote能不能由magazine里面的字符构成。如果可以,返回
true;否则返回false。
magazine中的每个字符只能在ransomNote中使用一次。
和242. 有效的字母异位词 - 力扣(LeetCode)有点类似,但是字母只能多不能少。
class Solution {
public:
unordered_map<char,int>mag_map;
bool canConstruct(string ransomNote, string magazine) {
for(char i : magazine){
mag_map[i]++; //记录mag中个字符出现次数
}
for(char j : ransomNote){
if(mag_map[j]>0){//看看 有没有or够不够
mag_map[j]--;//有 -1 代表用掉了
}
else return 0;//没有or已用完,不符合题意
}
return 1;
}
};
15. 三数之和 - 力扣(LeetCode)(记录-去重-收缩)
给你一个整数数组
nums,判断是否存在三元组[nums[i], nums[j], nums[k]]满足i != j、i != k且j != k,同时还满足nums[i] + nums[j] + nums[k] == 0。请你返回所有和为0且不重复的三元组。注意: 答案中不可以包含重复的三元组。
还在双指针!
先排序!先排序!先排序!
遍历一次数组,把遍历到的每个元素都设为头,在剩余数组里使用左右双指针
例如
i走到2,左右指针初始位置分别如上图,并向中间合拢,满足条件后会返回{nums[i], nums[left], nums[right]}。
注意!记录一个结果后,还应继续移动直到left>=right!!!可能还有其他组合情况满足三数之和为0
概括为:记录-去重-收缩
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> ans;
sort(nums.begin(),nums.end());
int size = nums.size();
int left = 0;
int right = 0;
for(int i = 0 ; i<=size-3 ; i++){
if(i>0){
if(nums[i]==nums[i-1]) continue;
}
// int left = (i==0 ? 1:0); //这么写不对,左右指针都在i之后的区间
// int right = (i==size-1 ? size-2:size-1); //同上
left = i+1;
right = size-1;
//************************************************************
//这个想法很蠢,找到一个符合题意的组合就会退出循环,而且条件判断混乱
// while(nums[i]+nums[left]+nums[right]&&right>left){
// if(nums[i]+nums[left]+nums[right]>0){
// right--;
// }
// else left++;
// }
// if(nums[i]+nums[left]+nums[right]==0&&left<right){
// ans.push_back({nums[i],nums[left],nums[right]});
// }
//************************************************************
while(left<right){
if(nums[i]+nums[left]+nums[right]==0){
// 找到三数之和为0的情况,记录,但不会退出while
ans.push_back({nums[i],nums[left],nums[right]});
//**************去重******************************
while(left<right&&nums[left]==nums[left+1]){
left++; //左去重
}
while(left<right&&nums[right]==nums[right-1]){
right--; //右去重
}
//************************************************
right--;
left++;
}
else if(nums[i]+nums[left]+nums[right]>0){
right--;
}
else left++;
}
}
return ans;
}
};
头去重
当nums[i]和nums[i-1]的值相同时,对nums[left]、nums[right]的要求是一样的,但i相比i-1向后走了一步,对应left和right的区间短了一步,因此符合条件的结果一定是i-1的子集。
因此在nums[i]=nums[i-1]前提下,i已经没有必要再处理,是上一步处理的i-1情况的子集。
代码描述如下。
for(int i = 0 ; i<=size-3 ; i++){
if(i>0){ if(nums[i]==nums[i-1]) continue;
//后续程序...
}
注意不是if (nums[i] == nums[i + 1])continue;
这相当于不处理当前的i,而是处理i+1,但i+1是i的子集,可能会漏解
尾去重
1.
找到符合题意的三个数后,不会立马退出
while并移动i,而是继续移动left和right,寻找其他可能。
2.
如果不进行去重处理,会直接执行right--; left++;
搭配去重处理,自增自减的逻辑才完美,这里草率一点也这么处理
因为去重后,数组严格单增,同时移动
left和right才可能使和不变
3.
下一次进入while循环体,if(nums[i]+nums[left]+nums[right]==0)成立,执行ans.push_back({nums[i],nums[left],nums[right]});在上图的特例中,增加了和上次一模一样的数组进入答案ans,显然不符合题意。
因此需要进行去重,代码描述如下。
if(nums[i]+nums[left]+nums[right]==0){
// 找到三数之和为0的情况,记录,但不会退出while
ans.push_back({nums[i],nums[left],nums[right]});
//**************去重******************************
while(left<right&&nums[left]==nums[left+1]){
left++; //左去重
}
while(left<right&&nums[right]==nums[right-1]){
right--; //右去重
}
//************************************************
//收缩,继续找
right--;
left++;
}
18. 四数之和 - 力扣(LeetCode)(记录-去重-收缩)
给你一个由
n个整数组成的数组nums,和一个目标值target。请你找出并返回满足下述全部条件且不重复的四元组[nums[a], nums[b], nums[c], nums[d]](若两个四元组元素一一对应,则认为两个四元组重复):
0 <= a, b, c, d < na、b、c和d互不相同nums[a] + nums[b] + nums[c] + nums[d] == target你可以按 任意顺序 返回答案 。
和三数之和一致,多一层循环。
排序!!!
注意内层循环for(int i = k+1;i<size;i++)避免重复
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> ans;
sort(nums.begin(), nums.end());
int size = nums.size();
int left = 0;
int right = size-1;
for(int k = 0;k<size;k++){
if(k>0){ //k的头去重
if(nums[k]==nums[k-1]) continue;
}
for(int i = k+1;i<size;i++){//从k+1开始,类似上三角矩阵,确保不会重复
if(i>k+1){ //i的头去重
if(nums[i]==nums[i-1]) continue;
}
left = i+1;
right = 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{ //==target,符合题意,先记录,再去重,再收缩
ans.push_back({nums[k],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 ans;
}
};
补充
后面两题没写剪枝