四数相加 II
要点
- 将
a+b转为map的形式存储其相加的所有信息key:a+b ; value:次数 - 再用
c+d去判断是否存在四数之和为0 a+b转map的时候存在重复,体现在value表示次数上;c+d两层for循环也存在重复
map写法
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2,
vector<int>& nums3, vector<int>& nums4) {
unordered_map<int, int> AplusB;
for (int a : nums1){
for (int b : nums2){
AplusB[a+b]++;
}
}
int count = 0;
for (int c : nums3){
for (int d : nums4){
if (AplusB.count(-(c+d))) {
count += AplusB[-(c+d)]; //含重复的数对,但按下标元组看是不重复
}
}
}
return count;
}
};
总结
暴力法复杂度为n^4,分为2+2就是n^2 + n^2复杂度,所以是22分不是13或者31分。
383. 赎金信
要点
- 用数组做哈希表,
242.有效的字母异位词的进阶题
数组哈希写法
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int mag[30] = {0};
//录入magazine
for (int i = 0; i < magazine.size(); i++) {
mag[magazine[i]-'a']++;
}
//检测ransomNote
for (int i = 0; i < ransomNote.size(); i++) {
if (mag[ransomNote[i]-'a'] > 0) {
mag[ransomNote[i]-'a']--;
}else {
return false;
}
}
return true;
}
};
总结
magazine中的字符每个只能用一次,思路还是很清晰的
15.三数之和
要点
a+b+c=0,a去重比较的是前一个元素,以免错过{-1, -1, 2}这样的三元组b, c的去重放在写入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()-2; i++){
int left = i+1;
int right = nums.size()-1;
if (nums[i] > 0) {
break;
}
//第一个元素去重,和前一个元素比相等,而不是后一个(先找,别着急跳)
if (i>0 && nums[i]==nums[i-1]) {
continue;
}
while (left < right) {
if (nums[i]+nums[left]+nums[right] < 0) left++; //--
else if (nums[i]+nums[left]+nums[right] > 0) right--; //--
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--;
//两边内收,继续找b c
left++;right--;
}
}
}
return result;
}
};
总结
对三个元素去重的思考,都是先完成当下的操作再考虑下一个是不是重复的需要跳过
双指针将暴力法的n^3降为n^2,还用哈希法的话,不好做去重
15.四数之和
要点
- 剪枝时,需要
nums[k] > 0,避免{-4, -2, -1, 2, 5} target = -5,这种加负数越加越小的情况,将存在解的枝剪掉 - bug1:
nums.size()-3,nums.size()-2这种写法,没有之前的判断,鲁棒性差 - bug2:int的范围是 - 2 ^ 31 ~2 ^ 31 - 1;也就是:[-2147483648, 2147483647] (10位),四项相加超过了int上限
双指针写法
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
if (nums.size() < 4) { //bug1
return result;
}
for (int k = 0; k < nums.size()-3; k++) { //bug1,原解的鲁棒性差,导致越界此类内存问题
//剪枝+去重
if (nums[k]>target && nums[k]>=0) break;
if (k>0 && nums[k]==nums[k-1]) continue; //k去重
for (int i = k+1; i < nums.size()-2; i++) {
if (nums[k]+nums[i] > target && nums[k]+nums[i] >= 0) break;
if (i>k+1 && nums[i]==nums[i-1]) continue; //i去重
int left = i+1;
int right = nums.size()-1;
while(left < right) {
if ((long) nums[k]+nums[i]+nums[left]+nums[right] > target) right--; //bug2
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]});
//left, right去重
while (left<right && nums[left] == nums[left+1]) left++;
while (left<right && nums[right] == nums[right-1]) right--;
left++;right--;
}
}
}
}
return result;
}
};
总结
思路同三数之和,就是要考虑4个指针k, i, left, right的去重,以及前两个用for遍历的指针的剪枝细节。