代码随想录算法训练营第6天|454.四数相加II、383. 赎金信、15. 三数之和、18. 四数之和
LeetCode454
2025-01-27 17:25:34 星期一
题目描述:力扣454
文档讲解:代码随想录(programmercarl)454.四数相加II
视频讲解:《代码随想录》算法视频公开课:学透哈希表,map使用有技巧!LeetCode:454.四数相加II
代码随想录视频内容简记
梳理
关于这道题目为什么需要用到哈希法?因为按照正常的解法,需要先遍历A和B两个数组,之后将所有找到的和存放到哈希表中,之后以同样的方式遍历C和D数组,并将对应的值进行查询。
关于为什么要使用map的结构?因为按照题目要求,最后求的是符合条件的四元组的个数,所以必须要有一个value值来对所有符合条件的情况进行记录。
大致代码内容
-
遍历A和B数组。
map[a+b]++
,将A和B遍历的结果存放到哈希表 -
遍历C和D数组。这里其实遍历的是之前A和B相加的结果是否在表中,所以需要查询
map[0-(c+d)]++
-
最后统计所有符合条件的value值的和
LeetCode测试
注意这个代码的时间复杂度为,代码比较简单,完整四数相加Ⅱ代码如下
点击查看代码
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]++;
}
}
for (int c : nums3) {
for (int d : nums4) {
if (map.find(0-(c+d)) != map.end()) {
count = count + map.find(0-(c+d))->second;
}
}
}
return count;
}
};
LeetCode383
题目描述:力扣383
文档讲解:代码随想录(programmercarl)383. 赎金信
梳理
-
这道题和242. 有效的字母异位词很相近。一开始是没有想到的,看了一眼文档讲解,说可以定义一个数组来操作,就明白了
-
首先遍历magazine字符串,将其所有的小写字母存放到哈希表中。其实感觉数组这个哈希结构和map有相似之处,只要是连续的地址,数组中也可以存放一个“value值”(当然并不是真的value)
-
之后遍历ransomNote字符串,将对应的元素全部进行
--
操作 -
最后再检查是否有元素小于0即可
LeetCode测试
点击查看代码
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int num[26] ={0};
for (int i = 0; i < magazine.size(); i++) {
num[magazine[i] - 'a']++;
}
for (int i = 0; i < ransomNote.size(); i++) {
num[ransomNote[i] - 'a']--;
}
for (int i = 0; i < 26; i++) {
if (num[i] < 0) return false;
}
return true;
}
};
LeetCode15
题目描述:力扣15
文档讲解:代码随想录(programmercarl)15. 三数之和
视频讲解:《代码随想录》算法视频公开课:梦破碎的地方!| LeetCode:15.三数之和
代码随想录视频内容简记
这个题用哈希法会比较麻烦,用双指针的思路会比较好解

梳理
-
首先需要对数组进行排序,按照从小到大升序排列
-
遍历a的值,然后是对
a
去重。针对于三个数,a,b,c
。定义一个循环遍历数组得到'a'
-
更新left指针和right指针
-
再内部定义一个循环遍历
b,c
的值 -
然后对
b,c
去重
大致代码内容
-
剪枝。
if (nums[i] > 0) continue
这种情况的话直接跳过,剪掉和不剪掉并不影响最后的的结果,但是剪掉可以减少缩短运行的时间。 -
对a去重不能使用
if (nums[i] == nums[i+1])continue
,这样会导致一个问题就是比如[-1,-1,2]这样的一个数组,数组内部有重复元素,这样的类型就会被直接跳过,所以需要用if (nums[i] == nums[i-1])
这样的方式 -
left = i + 1
,right = nums.size() - 1
。对数组的指针进行更新。 -
遍历b和c,
while (right > left)
,这里为什么不能加上等于号呢?因为按照题目要求,三数之和的索引是不可以相等的。
if (nums[i] + nums[left] + nums[right] > 0) right--;
else if (nums[i] + nums[left] + nums[right] < 0) left++;
else result.push_back(vector<int>{nums[i], nums[left], nums[right]});
- 接下俩要对b和c进行去重。因为比如涉及到这种情况
[-1,0,0,0,0]
,b和c的值重复,那么接下来不能再重复添加,所以while (right > left && nums[right - 1] == nums[right]) right--
;while (right > left && nums[left] == nums[left+1]) left++
,需要注意的是这里是一个循环,是while而不是if。同时需要注意:找到收获集之后,还需要进行left++
和right--
操作,保证下一次循环的b和c是不重复的。还有就是right > left
这个一定要加上,要不然没有循环的出口了,会出现超时错误
LeetCode测试
代码的时间复杂度是。感觉确实比较复杂,细节很多
点击查看代码
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
// 遍历a
for (int i = 0; i < nums.size(); i++) {
// 特殊情况直接跳过
if (nums[i] > 0) return result;
// 对a去重
if (i > 0 && nums[i] == nums[i - 1]) continue;
int left = i + 1;
int right = nums.size() - 1;
// 遍历b,c
while (right > left) {
// 对b,c去重
if (nums[i] + nums[left] + nums[right] > 0) right--;
else if (nums[i] + nums[left] + nums[right] < 0) left++;
else {
// 找到收获集
result.push_back(vector<int>{nums[i], nums[left], nums[right]});
// 为防止收获集出现重复
while (right > left && nums[right - 1] == nums[right]) right--;
while (right > left && nums[left + 1] == nums[left]) left++;
// 找到答案时再同时收缩
left++;
right--;
}
}
}
return result;
}
};
LeetCode18
题目描述:力扣18
文档讲解:代码随想录(programmercarl)18. 四数之和
视频讲解:《代码随想录》算法视频公开课:难在去重和剪枝!| LeetCode:18. 四数之和
代码随想录视频内容简记
四数之和的思路和三数之和一摸一样,难点就在于多了一个k需要遍历,同时增加的剪枝和去重操作比较繁琐

梳理
-
首先仍然是对数组进行排序
-
一级剪枝
-
一级去重
-
二级剪枝
-
二级去重
-
和三数之和代码相同的部分
大致代码内容
-
对于四个数,首先用k对第一个数进行遍历
for (int k = 0; k < nums.size(); k++)
,剪枝,if (nums[k] > target && target > 0 && nums[k] > 0) continue;
,这是一级剪枝。至于为什么这里不能出现负数,而要target > 0 && nums[k] > 0
?是因为如果一但出现负数,那么两个数相加就有可能变小,这是我们不希望看到的,违背了我们给他排序的本意。举个例子:[-4,-1,0,0],而target
是-5,这样就没法进行剪枝了,所以我们必须要保证为正数。 -
一级去重则是延续三数之和的思想,
if (k > 0 && nums[k] == nums[k-1]) continue;
-
对
i
进行遍历,for (int i = k + 1; i < nums.size(); i++)
。首先也是剪枝,if (nums[i] + nums[k] > target && target > 0 && num[i] + nums[k] > 0) continue
-
二级去重,需要进行的操作是
if (i > k + 1 && nums[i] == nums[i-1]) continue;
-
剩下的部分仍然按照三数之和的思路进行编写
LeetCode测试
测试的时候有一个问题需要注意,正常的代码在left和right指针向中间靠拢的部分,卡在了第284个用例上,提示int型不足够计算的问题。需要在代码中添加long
的转换if ((long) nums[k] + nums[i] + nums[left] + nums[right] > target) right--;
时间复杂度是,其实就是三个嵌套循环,如果看元素被操作的次数看不出来的话,就看循环之间的关系。完整四数之和代码如下:
点击查看代码
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
for (int k = 0; k < nums.size(); k++) {
if (nums[k] > target && nums[k] > 0 && target > 0) continue;
if (k > 0 && nums[k] == nums[k-1]) continue;
for (int i = k + 1; i < nums.size(); i++) {
if (nums[i] + nums[k] > target && nums[i] + nums[k] > 0 && target > 0) continue;
if (i > k + 1 && nums[i] == nums[i-1]) continue;
int left = i + 1;
int right = nums.size() - 1;
while (right > left) {
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 {
result.push_back(vector<int>{nums[k], 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--;
}
}
}
}
return result;
}
};
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端