您现在的位置是:首页 >其他 >代码随想录算法训练营第七天|454.四数相加II 383.赎金信 15.三数之和 18.四数之和网站首页其他
代码随想录算法训练营第七天|454.四数相加II 383.赎金信 15.三数之和 18.四数之和
目录
LeeCode 454.四数相加II
思路:
定义一个unordered_map,key 放 a 和 b 两数之和,value 放 a 和 b 两数之和出现的次数。
遍历 nums1 和 nums2 数组,统计两个数组元素之和,并将和出现的次数,放到 map 中。
定义 int 变量 count,用来统计 a+b+c+d = 0 出现的次数。
遍历 nums3 和 nums4 数组,如果 0-(c+d) 在map中出现过的话,就用 count 把 map 中 key 对应的 value 即出现次数统计出来。
最后返回统计值 count 。
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int,int> umap;
for(int a : A) {
for(int b : B) {
umap[a + b]++;
}
}
int count = 0;
for(int c : C) {
for(int d : D){
if(umap.find(0 - (c + d)) != umap.end()){
count += umap[0 - (c + d)];
}
}
}
return count;
}
};
LeeCode 383.赎金信
思路:题目要求等价于求前一个字符串能否由后一个字符串中的元素组成 ,与 242.有效字母的异位词 解题思路大体一致。
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int record[26] = {0};
if(ransoNote.size() > maganize.size()) {
return false;
}
for(int i = 0;i < maganize.length();i++) {
record[magazine[i]-'a']++;
}
for(int i = 0;i < ransoNote.length();i++){
record[ransoNote[i]-'a']--;
if(record[ransoNote[i]-'a'] < 0){
return false;
}
}
return true;
}
};
时间复杂度:O(n) 空间复杂度:O(1)
LeeCode 15.三数之和
思路: 双指针法
将数组排序,从下标 0 开始遍历数组,定一个下标 left 定义在 i+1 的位置上,定义下标 right 在数组结尾的位置上。在数组中找到 nums[i] + nums[left] +nums[right] = 0, 如果 nums[i] + nums[left] + nums[right] > 0 说明 此时三数之和大了,所以right下标向左移动。反之,如果 nums[i] + nums[left] + nums[right] < 0 说明 此时三数之和小了,left 就向右移动,直到left与right相遇为止。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
sort(nums.begin(),nums.end());
for(int i = 0; i < nums.size(); i++) {
//若排序后第一个元素>0,直接输出result
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;
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进行去重操作
while(right > left && nums[right] == nums[right - 1]) right--;
while(right > left && nums[left] == nums[left + 1]) left++;
//找到答案后,双指针收缩
right--;
left++;
}
}
}
return result;
}
};
时间复杂度:O(n²)
LeeCode 18.四数之和
思路:双指针法
与上一题三数之和本质相同,先确定前两个数,然后用双指针寻找剩下两个数。
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) {
break;
}
if (k > 0 && nums[k] == nums[k - 1]) {
continue;
}
for (int i = k+1; i < nums.size(); i++) {
if (nums[k] + nums[i] >target && nums[k] + nums[i] >= 0) {
break;
}
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]});
//对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;
}
};
时间复杂度:O(n³)
总结:
1. for(int a : A) 此语句意为将数组 A 的每一个值依次赋给 a 。
2. 去重操作时需要注意判断 nums[i] 与 nums[i - 1]是否相同而非判断 nums[i] 与 nums[i-1] 是否相同,后者可能会跳过{-1,-1,2}类似的数据。
3.利用双指针将三数之和暴力解法的O(n³)的复杂度降为 O(n²),将四数之和的复杂度从O(n^4)降为O(n³),类比下去,也可以解决五数之和 / 六数之和 等。
4.哈希表:
数组作哈希表:数据的范围已经确定 / 只有小写字母 ,此时使用数组比使用map 简单直接有效。
set作哈希表:没限制数据大小。三种数据结构中,std::set和std::multiset底层实现都是红黑树,std::unordered_set的底层实现是哈希, 使用unordered_set 读写效率是最高的。
map作哈希表:map是一种 <key,value> 的结构,在未限制数据大小,需要用 key 保存数值,用 value 保存数值所在下标的情况下, 使用map最合适。
5.用哈希法可以解决两数之和,用哈希法也可以解决三数之和、四数之和。但是非常麻烦,需要去重导致代码效率很低。故通过双指针法来求解。