代码随想录训练营day9|第454题.四数相加II,383. 赎金信,第15题. 三数之和,
第454题.四数相加II
题目链接:第454题.四数相加II
题目描述:给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。
输入: 输出: 2
A = [ 1, 2]
B = [-2,-1]
C = [-1, 2]
D = [ 0, 2]
解释:
两个元组如下:
(0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
(1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0
思路
本题是四个独立的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不用考虑有重复的四个元素相加等于0的情况,所以相对于题目18. 四数之和,题目15.三数之和,还是简单了不少!
1. 使用哈希表里的unordered-map,key放a和b两数之和,value 放a和b两数之和出现的次数。
2. 遍历大A和大B数组,统计两个数组元素之和,和出现的次数,放到map中
3. 定义int变量count,用来统计 a+b+c+d = 0 出现的次数。
4. 再遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就用count把map中key对应的value也就是出现次数统计出来。
代码展示
//四数相加Ⅱ
与四数之和不一样
class Solution {
public:
int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D){
unordered_map(int , int )map;
for(a:A){//遍历前两个数组去统计a+b
for(b:B) {
map[a+b]++;//用map去映射,并且value加一
}
}
// 在遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就把map中key对应的value也就是出现次数统计出来。
for (int c : C) {
for (int d : D) {
target = 0 - (c+d);//这就是要查的目标值,因为加起来才为0
if (umap.find(0 - (c + d)) != umap.end()) {、、//在遍历过程中找到target
count += map[target];//这里的value出现的次数就是符合条件按的键值数,key即对应的数值
}
}
}
return count;
} }
383. 赎金信
题目链接:383. 赎金信
题目描述:给定一个赎金信 (ransom) 字符串和一个杂志(magazine)字符串,判断第一个字符串 ransom 能不能由第二个字符串 magazines 里面的字符构成。如果可以构成,返回 true ;否则返回 false。
你可以假设两个字符串均只含有小写字母。
canConstruct("a", "b") -> false
canConstruct("aa", "ab") -> false
canConstruct("aa", "aab") -> true
思路
直接暴力解法--两层for循环,不断去查找。代码如下:
暴力解法:
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
for(i = 0; i < magazine.size(); i++){//bian li A
for(j = 0; j < ransonNote.size(); j++){//bianliB
if(ma[i] == ran[j]){//在遍历ran里找到和ma数组的相同的字符
ran.erase(ran.begin() + j);//删除掉相同的字符
break;
}
}
}
if(ran.size() = 0){//如果ran数组为空,那么ma的字符就直接组成ran
return true;
}
return false;
}
}
哈希解法
依然是数组在哈希法中的应用。
那么为什么用数组你???用数组干啥,都用map完事了,其实在本题的情况下,使用map的空间消耗要比数组大一些的,因为map要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效!
代码如下
//哈希法解赎金
题目所只有小写字母,那可以采用空间换取时间的哈希
策略, 用一个长度为26的数组还记录magazine里字母出现的次数
再用ransomNote去验证这个数组是否包含了ransomNote所需要的所有字母
class Solution {
public:
bool canConstruct(string ransomNote, string magazine){
int record[26];
if(ran.size( > mag.size())){//ran数组若大于mag数组,直接gg
return false;
}
if(int i = 0; i < mag.seze(); i++ ){
record[magazine[i]-'a'] ++;// 通过recode数据记录 magazine里各个字符出现次数
}
for(int j = 0; j < ran.size(); j++){
record[ransomNote[j]-'a']--;// 遍历ransomNote,在record里对应的字符个数做--操作
if(record[ran[j] - 'a'] < 0){
return false;// 如果小于零说明ransomNote里出现的字符,magazine没有
}
}
return true;
}
}
15.三数之和
题目链接:15.三数之和
题目描述:给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请
你返回所有和为 0 且不重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ]
思路
使用哈希法非常棘手,去重的过程不好处理,有很多小细节,如果在面试中很难想到位。
时间复杂度可以做到O(n^2),但还是比较费时的,因为不好做剪枝操作。
双指针法
拿这个nums数组来举例,首先将数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。
依然还是在数组中找到 abc 使得a + b +c =0,我们这里相当于 a = nums[i],b = nums[left],c = nums[right]。
接下来如何移动left 和right呢, 如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。
如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。
时间复杂度:O(n^2)。
代码实现
//第15题. 三数之和
判断 nums 中是否存在三个元素 a,b,c ,使得
a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result; //定义一个二维数组
sort(nums);从小到大进行一个排序
for(i = 0; i < nums.size(); i++){//i开始从第一个位置遍历数组
if(nums[i] > 0){
return false;//如果第一个数就大于0 后面的b加c 就不能为=0了
}
if(i > 0 && nums[i] ==nums[i - 1]) {
continue;//这里就是进行对i的去重操作,注意是 nums[i] ==nums[i - 1]才能跳过进入下一次循环
}
left = i + 1;
right = nums.size() - 1;
while(right > left){//若rig = left 说明指向同一个元素,则没有满足abc三个数,不能行
if (nums[i] + nums[left] + nums[right] > 0) right--;//移动指针
else if (nums[i] + nums[left] + nums[right] < 0) left++;//移动指针
else{//即等于0时的情况
result.push(vector<int>{nums[i], nums[left], nums[right]});//收获三元组结果
// 去重逻辑应该放在找到一个三元组之后,对b 和 c去重
while (right > left && nums[right] == nums[right - 1]) right--;//right和前一位相等则 左移
while (right > left && nums[left] == nums[left + 1]) left++; //left和后一位相等 右移
// 收获结果时,双指针同时收缩
right--;
left++;
}
}
}
return result;
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构