代码随想录第六天|Leetcode 454 四数相加II,383 赎金信,(*)15 三数之和,(*)18 四数之和
Leecode 454.四数相加II
题目链接 454 四数相加II
文章链接 https://www.programmercarl.com/0454.%E5%9B%9B%E6%95%B0%E7%9B%B8%E5%8A%A0II.html
视频链接 https://www.bilibili.com/video/BV1Md4y1Q7Yh
思路
可以把四数相加变成两数相加,问题就转换为和昨天差不多的两数相加。先挑任意两个数组
,计算两个数组对应的数的和,作为键存储,值为出现次数,剩下两个数组同理,判断map[target-和]是否出现过即可
代码
class Solution {
//四数之和可以转换成两数之和,分组
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int,int> map;
int count = 0;
for(int a:nums1){
for(int b:nums2){
map[a+b]++;
}
}//将任意两个数组每个值分别相加的和作为key存入map,value为出现的次数;
for(int c:nums3){
for(int d:nums4){
auto it = map.find(0-(c+d));
if(it!=map.end()){
count+=map[0-(c+d)];
}else{
continue;
}
}
}//计算剩下两个数组每个值分别相加的和,判断target-和是否已经出现,出现则计算出现次数
return count;
}
};
Leetcode 383 赎金信
题目链接 383 赎金信
文章链接 https://www.programmercarl.com/0383.%E8%B5%8E%E9%87%91%E4%BF%A1.html
思路
和LCR 032. 有效的字母异位词相似,判断magazine中的字符能不能构成ransomNote,就是看对应的字符够不够即可,用哈希比较简单
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
vector<int> record(26,0);
for(char c:ransomNote){
record[c-'a']++;//记录r中每个字符的出现次数
}
for(char c:magazine){
record[c-'a']--;//如果magazine也出现和r中相同的字符,数目减1,如果数目够的话,最后的值应该是小于等于0的,否则数目不够不能构成
}
for(int i=0;i<26;i++){
if(record[i]>=1){//大于等于一说明r中有的字符在m中数目不够,所以不能构成
return false;
}
}
return true;
}
};
Leetcode 15 三数之和
题目链接 15 三数之和
文章链接 https://www.programmercarl.com/0015.%E4%B8%89%E6%95%B0%E4%B9%8B%E5%92%8C.html
视频链接 https://www.bilibili.com/video/BV1GW4y127qo
思路
这道题和之前的四数之和II,[两数之和是有区别的,前面的题都是求下标,而且四数之和II不在同一个数组中,所以组合重复也没关系。而这道题就要考虑去重条件了,用哈希不太好做,且这道题目问的是组合的情况而不是下标,就说明数组可以排序,用双指针更快一些,下面的四数之和也是同理。
- 过程:拿这个nums数组来举例,首先将数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。依然还是在数组中找到 abc 使得a + b +c =0,我们这里相当于 a = nums[i],b = nums[left],c = nums[right]。如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。
- 细节:去重 要想每个组合不重复,就需要对每个数进行去重处理
- 第一个数的去重处理:nums[i-1]=nums[i],这里不能是nums[i+1]=nums[i](对应情况:{-4,-4,3})
- 第二个数的去重处理:第二个数是由left决定的,所以为nums[left]=nums[left+1] left++
- 第三个数的去重处理:nums[right]=nums[right-1] right–
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
//使用双指针的前提:数组可以排序
vector<vector<int>> result;
sort(nums.begin(),nums.end());
int left=0,right=nums.size()-1;
int flag = 0;
for(int i=0;i<nums.size()-2;i++){
//剪枝
if(nums[i]>0) return result;
//去重第一个数
if(i>=1&&nums[i]==nums[i-1]){
continue;
}
left = i+1;
right = nums.size()-1;
//条件是大于,不能相等的原因是一个元素不能使用两次
while(right>left){
//和大了就让right向左移动
if(nums[i]+nums[left]+nums[right]>0){
right--;
//和小了就让left向右移动
}else if(nums[i]+nums[left]+nums[right]<0){
left++;
}else{
result.push_back({nums[i],nums[left],nums[right]});
//当找到三元组后,还需对剩下两个数去重
while(right>left&&nums[right]==nums[right-1]) right--;
while(right>left&&nums[left]==nums[left+1]) left++;
right--;
left++;
}
}
}
return result;
}
};
本题收获
这道题想了很久一点思路没有,看了卡哥的题解才知道可以用双指针做,还是学死了,不会灵活使用之前学过的算法。看了题解之后去重条件理解了一段时间,之前我所想到的是暴力遍历去重,使用双指针并且对数组排序后去重就方便很多还是要多复习这道题
Leetcode 18 四数之和
题目链接 18. 四数之和
文章链接 https://www.programmercarl.com/0018.%E5%9B%9B%E6%95%B0%E4%B9%8B%E5%92%8C.html
视频链接 https://www.bilibili.com/video/BV1DS4y147US
思路
和三数之和的思路一模一样了,只不过这次相当于多了一层循环
15.三数之和 的双指针解法是一层for循环num[i]为确定值,然后循环内有left和right下标作为双指针,找到nums[i] + nums[left] + nums[right] == 0。
四数之和的双指针解法是两层for循环nums[i] + nums[j]为确定值,依然是循环内有left和right下标作为双指针,找出nums[i] + nums[j] + nums[left] + nums[right] == target的情况,三数之和的时间复杂度是O(n^2),四数之和的时间复杂度是O(n^3) 。
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result;
//对数组排序
sort(nums.begin(),nums.end());
int n = nums.size();
int left,right=n-1;
for(int i=0;i<n-3;i++){
//如果排序之后nums[i]大于target并且nums[i]大于等于0,则目标值不可能存在,剪掉这条路径
if(nums[i]>target&&nums[i]>=0) break;
//nums[i]去重
if(i>=1&&nums[i]==nums[i-1]) continue;
for(int j=i+1;j<n-2;j++){
//如果此时nums[i]+nums[j]大于target并且二者之和不小于0,则目标值不可能存在,剪掉这条路径
if(nums[j]+nums[i]>target&&nums[j]+nums[i]>=0) break;
//nums[j]去重
if(j>=i+2&&nums[j]==nums[j-1]) continue;
left = j+1;
right = n-1;
while(left<right){
//这里必须转换一下数据类型,int可能不太够
if((long)nums[i]+nums[left]+nums[right]+nums[j]>target){
right--;
}else if((long)nums[i]+nums[left]+nums[right]+nums[j]<target){
left++;
}else{
result.push_back({nums[i],nums[left],nums[right],nums[j]});
while(left<right&&nums[left]==nums[left+1]) left++;
while(left<right&&nums[right]==nums[right-1]) right--;
left++;
right--;
}
}
}
}
return result;
}
};