LeetCode 第454题.四数相加II
分析:先计算num1与nums2的和,并将其存储到哈希结构为unordered_map的容器m中,key作为两数之和,value是和出现的次数。
再计算nums3和nums4的和,在m中找(0-(nums1+nums2)),如果能找到,说明存在和为0的四个数,最后统计满足和为0的元组的个数。
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int,int>m;//key:a+b的数值,value:a+b数值出现的次数
for(int i = 0 ;i < nums1.size();i++){
for(int j = 0;j < nums2.size();j++){
//统计nums1和nums2和的值及出现次数
m[nums1[i]+nums2[j]]++;
}
}
int count = 0; //统计满足和为零的元组的个数
for(int c : nums3){
for(int d : nums4){
//如果0-(nums3+nums4)的差能够在m中找到
if(m.find(0-(c + d)) != m.end()){
count += m[0-(c+d)];
}
}
}
return count;
}
};
LeetCode 383. 赎金信
分析:在本题中用数组作为哈希表,索引代表字母,索引对应的数组元素记录该字母出现的次数。若ransomNote中的字母有在magazine中出现(字母相同,magazine[i]-'a'和ransomNote - 'a'相等相同并作为索引,则该数组对应的索引记录了字母出现的次数),该索引(即字母)对应的数组元素--。
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
//创建一个数组接收字母
int record[26] = {0};
//先判断ransomNote数组大小是否比magazine数组大
if(ransomNote.size() > magazine.size()){
return false;
}
//对每一个字母,记录其出现次数
for(int i = 0;i < magazine.size();i++){
record[magazine[i] - 'a']++;
}
//若ransomNote中的字母有在magazine中出现(字母相同,magazine[i]-'a'和ransomNote - 'a'相等相同并作为索引,则该数组对应的索引记录了字母出现的次数)
for(int i = 0;i < ransomNote.size();i++){
record[ransomNote[i] - 'a']--;
if(record[ransomNote[i] - 'a'] < 0){
return false;
}
}
return true;
}
};
LeetCode 15. 三数之和
双指针法分析:(1)由于不需要返回数组元素下标,因此先对数组进行排序
(2)i是第1个元素的下标,首先判断排序后的第1个元素是否大于0,如果是,那么一定不会存在和为0的三个数,直接返回结果;
对第一个数去重:首先,确定第1个数以后,通过循环进行第2、3个数的选择,找出与第1个数的和为零的所有组合;结束后,对下标+1,找到下一个第1个数,如果这个数和前一个数相同,那么已经存在了所有和为0的组合,因此跳出当前i循环,进行i+1循环。
一种错误的去重a方法:
// 错误去重a方法,将会漏掉-1,-1,2 这种情况 /* if (nums[i] == nums[i + 1]) { continue; }
例如一个三元组{-1,-1,2},如果进行上述continu,那么用于确定left和right的i就是第二个-1的下标,此时将忽略组合{-1,-1,2},同时left和right是同一个值的下标,不满足三元组来自三个数的要求。
对于第1个数,要保证选取的是重复数中出现的第一个数,例如{-2,,-1,-1,-1,0,1,2,3,3,3}选取下标为1的-1。
(3)选择第2、3个数:在第1个数的下标确定的情况下,判断nums[I] + nums[left] + nums[right]的和;大于0,那么right左移(和变小);小于0,那么left右移(和变大);等于0,将三个数构成的三元组存入result;
(4)对第2、3个数去重:当出现nums[I] + nums[left] + nums[right] = 0时,进行left++、right--,找还有没有和当前第1个数nums[i]的和为0的数,如果在left < right 的情况下,nums[left+1] = nums[left],那么nums[right]是固定的,不可能会出现新的和为0的组合,因此要left++;因为数组是排好序的,相同的数都是连续存储的,因此采用while循环,跳过所有和当前第2个数相等的数。
第3个数去重也是同理。
去重逻辑要放在给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++){
//如果排序后第一个数字大于0,那么直接返回结果
if(nums[i] > 0){
return result;
}
//去重
if(i > 0 && nums[i] == nums[i-1]){
continue;
}
//定义双指针left 和right
int left = i+1;
int right = nums.size() - 1;
while(left < right){
if(nums[i] + nums[left] + nums[right] > 0){
right--;
}
else if(nums[i] + nums[left] + nums[right] < 0){
left++;
}
else{
//将和为0的三元组存入容器
result.push_back(vector<int>{nums[i],nums[left],nums[right]});
//在找到一个三元组时,对left和right去重
while(right > left && nums[left] == nums[left + 1]){
left++;
}
while(right > left && nums[right] == nums[right - 1]){
right--;
}
left++;
right--;
}
}
}
return result;
}
};
LeetCode18. 四数之和
分析:采用双指针法计算三数之和、四数之和乃至五数之和、六数之和,最重要是确定left和right,是寻找最后两个数的索引。之前的数都可以用for循环进行剪枝、 去重后寻找。
与三数之和不同点在于:(1)目标target是任意数,不是0,在进行第1个数剪枝操作时,如果满足nums[i] > target && target >0 那么后面就不会有满足四数之和等于target的数了;同理,在进行第2个数剪枝操作时,满足nums[i] + nums[j] > target && target > 0,找不到符合要求的数。这是因为要考虑到负数的情况。
(2)要考虑整型溢出越界问题:nums[i] + nums[j] + nums[left] + nums[right]溢出,要使用long
变为:(long)nums[i] + nums[j] + nums[left] + nums[right].
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 && target > 0 && nums[i] > 0){
break;//没有符合和为target的四个数,跳出循环
}
//去重
if(i > 0 && nums[i] == nums[i-1]){
continue;
}
//对第二个数进行操作
for(int j = i+1; j < nums.size(); j++){
//剪枝
if(nums[i] + nums[j] > target && target > 0){
break;
}
//去重
if(j > i+1 && nums[j] == nums[j-1]){
continue;
}
int left = j+1;
int 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]});
//对当前索引为left的元素去重
while(left < right && nums[left] == nums[left + 1]){
left++;
}
while(left < right && nums[right] ==nums[right - 1]){
right--;
}
right--;
left++;
}
}
}
}
return result;
}
};