新的一周又是补进度的一周。。。
每道题的方法:
最后总结写
题目一链接:454. 四数相加 II - 力扣(LeetCode)
思路:先便利a,b数组,统计a + b出现的次数,再便利c,d数组,看umap映射里是否出现0 - (c + d) 的值,如果出现,就让count加上umap里值等于0 - (c + d)的个数。。。
代码:
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map <int,int> umap;
//先便利a,b数组,统计a + b出现的次数
for(int a : nums1) {
for(int b : nums2) {
umap[a + b] ++;
}
}
//在便利c,d数组,看umap映射里是否出现0 - (c + d) 的值,如果出现,就让count加上umap里值等于0 - (c + d)的个数
int count = 0;
for(int c : nums3) {
for(int d : nums4) {
if(umap.find(0 - (c + d)) != umap.end()) {
count += umap[0 - (c + d)];
}
}
}
return count;
}
};
难点:
解释细节1:因为a + b的值很大,如果用数组的话不好确定数组的大小。
解释细节2:map类似于数组,它的key是数组的下标而它的value则是数组的值
解释细节3:因为查询时用的是map而不是C,D两个数组,因此要查找的数是a + b的值
解释细节4:因为此题不需要去重,所以只需要统计二者由多少个符合条件的组合就行,所以需要加上每种可能的总数,即value值。
题目二链接:383. 赎金信 - 力扣(LeetCode)
思路:用到哈希表里的数组,将小写字母转化为26个数组下标然后进行计数
代码:
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int c[26] = {0};
for(int i = 0;i < ransomNote.size();i ++) {
c[ransomNote[i] - 'a'] ++;
}
for(int j = 0;j < magazine.size();j ++) {
c[magazine[j] - 'a'] --;
}
for(int i = 0;i < 26;i ++) {
if(c[i] > 0) return false;
}
return true;
}
};
难点:
题目三链接:15. 三数之和 - 力扣(LeetCode)
思路:利用双指针算法将所有符合条件并去重的结果填入二维数组result里,最后return result:首先利用i指针遍历数组nums,再用left和right指针分别从两侧向中间移动直到left > right,在此过程中通过left和right和i指针的移动进行去重。
代码:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
//开一个二维容器
vector<vector<int>> result;
//必须给nums先排序
sort(nums.begin(),nums.end());
for(int i = 0;i < nums.size();i ++) {
//先移动i同时去重
if(nums[i] > 0) return result;
if(i > 0 &&nums[i] == nums[i - 1]) continue;
int left = i + 1;
int right = nums.size() - 1;
//根据三数之和排除一些left和right的值
while(left < right) {
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]});
while(right > left && nums[left] == nums[left + 1]) left ++;
while(right > left && nums[right] == nums[right - 1]) right --;
left ++;
right --;
}
}
}
return result;
}
};
难点:
解释细节1:因为map统计出答案后不容易进行去重操作
操作细节3:因为i的右侧是left,如果让i = i + 1,i 有可能会等于left,而题目要求i与left,right互不相等
操作细节4:因为只有right向左移会使得三数之和变小,同理left
操作细节5:跟i去重其实是一个道理区别在于nums[left]和它右边的数比,,,
补充细节:当三个指针都经过去重后,那么最终答案就是去重的
题目四链接:18. 四数之和 - 力扣(LeetCode)
思路:首先创建一个二维数组容器result,然后对原来的数据进行排序,以便于后续利用双指针法;
然后开始双指针法:一共设置四个指针,这四个指针分三层,第一层是k,第二层是i,第三层是left和right;然后在第一层和第二层的指针中进行减枝和去重操作,第三层指针只进行去重操作,最后输出result.
代码:
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 ++) {
//第一层减枝:target可能是负数,而负数可能越加越小,因此必须保证target大于零才能用这个方法减枝
if(target >= 0 && nums[k] > target) break;
//第一层去重:
if(k > 0 && nums[k] == nums[k - 1]) continue;
else {
for(int i = k + 1;i < nums.size();i ++) {
//第二层减枝:原理同上
if(target >= 0 && nums[k] + nums[i] > target) break;
//第二层去重:if判断条件比对第一层去重
if(i > k + 1 && nums[i] == nums[i - 1]) continue;
else {
int left = i + 1;
int right = nums.size() - 1;
//移动left和right指针:
//防止加和溢出在前面加long:
while(left < right) {
if((long)nums[k] + nums[i] + nums[left] + nums[right] > target) right --;
//防止加和溢出在前面加long,long long 会慢一些
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]});
//第三层去重:注意while里面必须在right > left 的前提下再进行操否则会导致数组越界访问,此处的界指的是第一层while()里的判断条件
while(right > left && nums[left] == nums[left + 1]) left ++;
while(right > left && nums[right] == nums[right - 1]) right --;
left ++;
right --;
}
}
}
}
}
}
return result;
}
};
难点:
解释细节1:方便后续双指针算法
解释细节2:必须保证target大于零才能判断后续加和会越加越大,因为负数越加越小
解释细节3:首先i大于k,所以i的初始值必须大于k ,又因为i的右面有left所以nums[i]需要和nums[i - 1]比较,所以i必须大于k + 1;i的范围还可以参考第一层去重的条件
解释细节4:分别跟左右作比较
解释细节5:加和优先级大于(long)所以加前面就行,避免数超范围
解释细节6:如果去重里面的while不加上left<right,会导致数组越界访问