454 四数相加II
-
题意
- 给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。
- 为了使问题简单化,所有的 A, B, C, D 具有相同的长度 N,且 0 ≤ N ≤ 500 。所有整数的范围在 -2^28 到 2^28 - 1 之间,最终结果不会超过 2^31 - 1 。
- 例如:
- 输入: A = [ 1, 2],B = [-2,-1],C = [-1, 2],D = [ 0, 2]
- 输出: 2
- 解释:
- 两个元组如下:
- 1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
- 2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0
- 两个元组如下:
- 例如:
-
思路
- 将四个数的和分成两个组的和,即可视为两数之和同类型的题目;已知a+b的值,判断c+d有target-(a+b)的值,判断一个值有没有在集合中出现过->使用哈希表;
- 最终需要统计出现的次数(可以用下标来统计),因此不仅需要数的和value,更需要下标的key值;因此选择map;
- 最终的count计数:count+=value;而不是count++;因为符合条件的在集合内的a+b的值不只是一个,而是这个值出现的次数;
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 n1 : nums1) {//key:a + b的数值,value : a + b数值出现的次数
for (int n2 : nums2) {
map[n1 + n2]++;
}
}
for (int n3 : nums3) {
for (int n4 : nums4) {
if (map.find(0 - (n3 + n4)) != map.end())//找到了n3和n4对应的value
count += map[0 - (n3 + n4)];
}
}
return count;
}
};
383 赎金信
-
题意
- 给定一个赎金信 (ransom) 字符串和一个杂志(magazine)字符串,判断第一个字符串 ransom 能不能由第二个字符串 magazines 里面的字符构成。如果可以构成,返回 true ;否则返回 false。
- (题目说明:为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思。杂志字符串中的每个字符只能在赎金信字符串中使用一次。)
- 示例:
- 你可以假设两个字符串均只含有小写字母。
- canConstruct("a", "b") -> false
- canConstruct("aa", "ab") -> false
- canConstruct("aa", "aab") -> true
-
思路
- 暴力双循环:
- 两层遍历从magazine中删减对应的ransom的字母,如果ransom被删完了说明magazine全覆盖;
- 哈希表解法:
- 用一个长度为26的数组记录magazine里字母出现的次数,然后用这个数组来减去ransom中出现的次数更新数组,如果数组的某个value小于零,说明未能全覆盖;(类比有效的字母异位词)
- 暴力双循环:
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int record[26] = { 0 };
//先将magazine中的值加入到record中计数
for (int i = 0; i < magazine.length(); i++) {
record[magazine[i] - 'a']++;
}
for (int i = 0; i < ransomNote.length(); i++) {
record[ransomNote[i] - 'a']--;
if (record[ransomNote[i] - 'a'] < 0) return false;//说明magazine中的值不够ransomNote减的
}
//遍历完均为出现异常,则返回true
return true;
}
};
15 三数之和
-
题意
- 给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
- 注意: 答案中不可以包含重复的三元组。
- 示例:
- 给定数组 nums = [-1, 0, 1, 2, -1, -4],
- 满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ]
- 给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
-
思路
- 先使用排序对数组进行排序操作,使之从小到大排序
- 双指针思路:对数组进行遍历,对每一个当前数字,使用左指针指向当前数字的下一个,右指针指向尾部数字;nums[i] + nums[left] + nums[right] > 0,右指针左移;nums[i] + nums[left] + nums[right] <0,左指针右移,直到等于0;
- 去重操作:
- 对于遍历的每一个指针去重,i>0 && nums[i]==nums[i-1].continue;对i进行去重;
- 对于左指针和右指针去重:while (right > left && nums[left] == nums[left + 1]) left++;注意始终要加right>left的限制条件;right同理。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>>result;//创建一个二维数组
int left, right;
sort(nums.begin(), nums.end());
for (int i = 0; i < nums.size(); i++) {
if (nums[i] > 0) return result;//排序完第一个数就是正数,直接GG,返回result;
//去重操作1,对i进行去重
if (i > 0 && nums[i] == nums[i - 1]) continue;
//初始化双指针
left = i + 1;
right = nums.size() - 1;
while (right > left) {//三个数,nums[i],nums[left],nums[right],所以不能取等号
//双指针收缩寻找适合的三元素
if (nums[i] + nums[left] + nums[right] > 0) right--;
else if (nums[i] + nums[left] + nums[right] < 0) left++;
//在确定有一组正解之后,移动right和left进行对left和right进行去重
else {
result.push_back(vector<int>{nums[i], nums[left], nums[right]});
while (right > left && nums[left] == nums[left + 1]) left++;//left左移,nums[left]的值不变
while (right > left && nums[right] == nums[right - 1]) right--;//right右移,nums[right]的值不变
//找到了i对应的三要素且对left,right去重之后,双指针同时收缩,寻找当前i所对应的下一组left和right
right--;
left++;
//right和left收缩放在里面而不是外面,以为无论有没有找到合适的三要素,if ,else if,else对应的条件都会使得双指针收缩
}
}
}
return result;
}
};
18 四数之和
-
题意
- 给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
- 注意:答案中不可以包含重复的四元组。
- 示例: 给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。 满足要求的四元组集合为: [ [-1, 0, 0, 1], [-2, -1, 1, 2], [-2, 0, 0, 2] ]
- 给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
-
思路
- 双指针法,在三数之和的基础山再套一层for循环
- 排序:先排序,将数组按照从小到大的顺序排列
- 剪枝操作:算法优化,通过某种判断,避免不必要的搜索遍历;
- 一层剪枝:考虑到target可正可负,不能直接nums[k]>target(nums[k]>target && nums[k]>0)
- 二层剪枝:对nums[k]+nums[i]的和进行剪枝操作即nums[k]+nums[i]>target && nums[i]>0;
- 去重操作:考虑到题目要求四元组不重复,因此也需要像三数之和一样进行去重操作;
- 一层去重:if(k>0 && nums[k]==nums[k-1]);
- 二层去重:if(i>k+1 && nums[i]==nums[i-1]);
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>>result;
//先排序
sort(nums.begin(), nums.end());
//k < nums.size() - 3 和 i < nums.size() - 2会报错:nums.size()的返回值类型为无符号整数
for (int i = 0; i < nums.size(); i++) {
//第一层剪枝(对i)
//if (target > 0 && nums[i] > 0 && nums[i] > target)break;
//剪枝可进一步优化为如下,因为nums[i]之后的都是比nums[i]大,加起来肯定比target大,所以直接break终止当前的循环,后面的都不需要遍历了
if (nums[i] > 0 && nums[i] > target) break;
//第一层去重,参考三数之和
if (i > 0 && nums[i] == nums[i - 1]) continue;//跳过当前i,快进到下一个i;
for (int k = i + 1; k < nums.size(); k++) {
//第二层剪枝(对nums[i]和nums[k]的和)
//if (nums[k] + nums[i] >= 0 && nums[i] + nums[k] > target) break;
//优化如下:
if (nums[k] + nums[i] > target && nums[i] > 0)break;
//第二层去重
if (k > i+1 && nums[k] == nums[k - 1]) continue;
int left = k + 1;
int right = nums.size() - 1;
while (right > left) {
//long扩充int,防止nums =[0, 0, 0, 1000000000, 1000000000, 1000000000, 1000000000]这样的变态数据
if ((long)nums[i] + nums[k] + nums[left] + nums[right] > target) right--;
else if ((long)nums[i] + nums[k] + nums[left] + nums[right] < target) left++;
else {
result.push_back(vector<int>{ nums[i],nums[k],nums[left],nums[right] });
//对right和left去重
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1])left++;
right--;
left++;
}
}
}
}
return result;
}
};
总结
2024.1.2补的第二篇,哈希表结束,明天把字符串的两篇给补了~