感觉今天很烧脑。。。主要是三数之和理解思路还有四数之和debug过程有点费劲。
454.四数相加II
这个只会暴力。。这个题目不需要去重(因为需要统计的是下标),而且只需要返回符合条件的元组个数,看完视频讲解以后直接就做出来了,这个题目的关键点在于两两分组,前两组构造出一个哈希表,后两个数组用两层for循环遍历就可以,在符合条件的时候注意个数不应该加1,而是加上哈希表中对应存储的值。
这是我的代码。
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int, int> umap;
int result = 0;
for(int i = 0; i < nums1.size(); i++){
for(int j = 0; j < nums2.size(); j++)
umap[nums1[i] + nums2[j]] ++;
}
for(int i : nums3){
for(int j : nums4){
if(umap.find(0 - i - j) != umap.end()) //配对成功
result += umap[0 - i - j];
}
}
return result;
}
};
383. 赎金信
这题比较简单,有了之前的 242.有效的字母异位词基础,做这道题很快,思路是一样的。用一个大小为26的整型数组统计magazine中各个字母出现的次数,然后在遍历ransomNote的时候自减就可以。唯一要注意的点就是在最后判断整个数组的值的时候,当各个值都大于等于0时即可满足要求,但凡出现一个负数,都不符合要求,这个之前的有效的字母异位词判断条件有所不同。
这是我的代码
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int hash[26] = {0}; //全部初始化为0
for(char c : magazine)
hash[c - 'a'] += 1;
for(char c : ransomNote)
hash[c - 'a'] -= 1;
for(int i : hash){
if(i < 0)
return false;
}
return true;
}
};
15. 三数之和
这道题果断听劝,没有用哈希法去做,老老实实去看视频,但是在看视频的时候还是感觉理解起来有点困难,回放了几次才彻底弄明白。这道题需要去重,对遍历的nums[i]、nums[left]、nums[right]都需要去重,注意,在找到符合条件的元组后,还需要对left和right进行去重,去重后此时left和right实际上还指向本次的结果,一定要记得在去重之后再对left加一,对right减一,否则会陷入死循环,我就吃了这个亏,用VS debug才发现问题所在。。
这是我的代码
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
sort(nums.begin(), nums.end()); //对nums进行升序排序
for(int i = 0; i < nums.size(); i++){
if(nums[i] > 0)
return result;
if(i > 0 && nums[i] == nums[i - 1]) //对nums[i]去重
continue;
int left = i + 1;
int right = nums.size() - 1;
while(left < right){
if(nums[i] + nums[left] + nums[right] < 0) //和小了,left偏移
left++;
else if(nums[i] + nums[left] + nums[right] > 0) //和大了,right偏移
right--;
else{ //恰好符合
result.push_back({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;
}
};
18. 四数之和
这道题目比上一道题还要难。。。。但是思路大体差不多,我还是看了视频,主要是将这一题的nums[i] + nums[j]当成上一题的nums[i]就行了,大体的代码写起来很快,但是还有一些细节需要注意。
在双重for循环遍历数组时,需要在两重循环中都做一个去重操作,在内层循环中,i是不变的,主要是对j进行探索,但是经过一轮内层循环后,如果该位置的值和上一轮循环的值相同的话,就没必要继续了,应该直接continue;同理对于外层循环也需要对nums[i]去重。还有一些剪枝操作,但是这道题的判定条件要复杂一些,如果目标值是负数的话,nums[i] + nums[j] > target不能用来作为跳过的条件,因为后面可能还有负数,直接跳过的话可能错过符合条件的元组。但是有一些情况是可以跳过的,例如target > 0,而nums[i] + nums[j]已经大于target且nums[j] >= 0。
反正这道题我还是做了几十分钟。。最后还被一个很大很大的测试样例给恶心了。。记得及时强制类型转换为long int。。。
这是我的代码
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result;
sort(nums.begin(), nums.end()); //将nums升序排列
for(int i = 0; i < nums.size(); i++){
if(i > 0 && nums[i] == nums[i - 1])
continue;
for(int j = i + 1; j < nums.size(); j++){
if (j > i + 1 && nums[j] == nums[j - 1])
continue;
if(nums[i] + nums[j] > target && nums[j] >= 0) //没有必要继续找
break;
int left = j + 1;
int right = nums.size() - 1;
while(left < right){
if((long int)nums[i] + (long int)nums[j] + (long int)nums[left] + (long int)nums[right] < target) //和小了,left右移
left++;
else if((long int)nums[i] + (long int)nums[j] + (long int)nums[left] + (long int)nums[right] > target) //和大了,right左移
right--;
else{ //找到符合条件的结果
result.push_back({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;
}
};