代码随想录算法训练营day6| 454.四数相加II 383.赎金信 15.三数之和 18.四数之和
LeetCode 454 四数相加II
题目链接: 454.四数相加II
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) { //解题思路是将四个数组分为两个相邻数组的和,然后把其中一个数组的值进行映射,value存放key出现次数
unordered_map<int, int> umap; //定义map数组
int count = 0; //初始化符合条件结果
for(int i : nums1) {
for(int j : nums2) {
umap[i + j]++; //记录i+j的次数
}
}
for(int i : nums3) {
for(int j : nums4) {
if(umap.find(0 - i - j) != umap.end())
count += umap[0- i - j];
}
}
return count;
}
};
本题小结:将两两数组的和分为两个数组,然后把其中一个数组的值进行映射,最后进行判断计数累加即可。
LeetCode 383 赎金信
题目链接: 383. 赎金信
思路:本题判断第一个字符串ransom能不能由第二个字符串magazines里面的字符构成,但是这里需要注意两点:
- 第一点“为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思” 这里说明杂志里面的字母不可重复使用!
- 第二点 “你可以假设两个字符串均只含有小写字母。” 说明只有小写字母!!!
// 时间复杂度: O(n)
// 空间复杂度:O(1)
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int record[26] = {0}; //题目中假设仅含小写字母,定义计数数组
for(char s : magazine) {
record[s - 'a']++; //对出现过的字符计数
}
for(char s : ransomNote) {
record[s - 'a']--; //对相同字符减一
if(record[s - 'a'] < 0) //如果计数小于0,无法构成
return false;
}
return true;
}
};
本题小结:假设仅含小写字母,定义计数数组record,查找时进行减一操作,最后判断计数数组的值。
LeetCode 15 三数之和
题目链接: 15.三数之和
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
// 找出a + b + c = 0
// a = nums[i], b = nums[j], c = -(a + b)
for (int i = 0; i < nums.size(); i++) {
// 排序之后如果第一个元素已经大于零,那么不可能凑成三元组
if (nums[i] > 0) {
break;
}
if (i > 0 && nums[i] == nums[i - 1]) { //三元组元素a去重
continue;
}
unordered_set<int> set;
for (int j = i + 1; j < nums.size(); j++) {
if (j > i + 2
&& nums[j] == nums[j-1]
&& nums[j-1] == nums[j-2]) { // 三元组元素b去重
continue;
}
int c = 0 - (nums[i] + nums[j]);
if (set.find(c) != set.end()) {
result.push_back({nums[i], nums[j], c});
set.erase(c);// 三元组元素c去重
} else {
set.insert(nums[j]);
}
}
}
return result;
}
};
此种方法效率不高,应该使用双指针法 !
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++) {
if(nums[i] > 0) //因为对数组是有序排列的,如果第一个数大于零,则无法找到符合条件的三数
return result;
if(i > 0 && nums[i] == nums[i - 1]) //对第一个数进行去重
continue;
//双指针法
int left = i + 1, right = nums.size() - 1;
while(left < right) {
if(nums[i] + nums[left] + nums[right] > 0) //三数之和大于0
right--;
else if(nums[i] + nums[left] + nums[right] < 0) //三数之和小于0
left++;
else {
result.push_back(vector<int> {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 result;
}
};
本题小结:思路是先对数组进行排序,然后在一个循环中确定第一位数,通过双指针进行结果判断,但是需要进行剪枝优化。
LeetCode 18 四数之和
题目链接: 18. 四数之和
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result; //定义结果矩阵
sort(nums.begin(), nums.end()); //对数组进行排序
for(int i = 0; i < nums.size(); i++) {
//进行剪枝优化
if(nums[i] > target && nums[i] >= 0)
break;
//对第一个数进行去重
if(i > 0 && nums[i] == nums[i - 1])
continue;
for(int j = i + 1; j < nums.size(); j++) {
//剪枝优化
if(nums[i] + nums[j] > target && nums[i] + nums[j] >= 0)
break;
//对第二个数进行去重
if(j > i + 1 && nums[j] == nums[j - 1])
continue;
//双指针
int left = j + 1, right = nums.size() - 1;
while(left < right) {
if((long)nums[i] + nums[j] + nums[left] + nums[right] > target) //如果和大于目标,此处数据可能会溢出
right--;
else if((long)nums[i] + nums[j] + nums[left] + nums[right] < target) //如果和小于目标,此处数据可能会溢出
left++;
else { //保存到数组
result.push_back(vector<int> {nums[i], nums[j], 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 result;
}
};
本题小结:与上题思想类似,此题多加一个循环,两重循环确定两位数,然后再采用双指针进行判断,此题的剪枝优化需要多加思考!