今日总结:
1、在使用map的时候,可以使用迭代器:
//方式2:使用迭代器
unordered_map<int,int>::iterator it
auto it
也可以使用[ ]进行对应的key查找对应的value,可以使用自加自减。
//方式1:使用[]
map_num[x+i]++;
2、在使用map、set的时候可以使用num.erase(iter/x) ,iter是迭代器,删除这个集合中的迭代器位置的元素,x是元素 ,会删除这个集合中所有x的元素。
3、四数之和的减枝处理需要着重复习
四数相加
题目链接:LeetCode454、四数相加Ⅱ
整体思路:
因为判断有多少个A+B+C+D=0,所以是一个检测存在性的问题,可以使用哈希表实现
检测的结果是存在多少个A+B+C+D=0的值,四个变量太多-->需要将问题简化:将A+B、C+D的和分别遍历,判断有多少次A+B+C+D=0-->
可以使用无序映射unordered_map,key记录A+B的值,value记录A+B的值出现的次数
通过判断map中存在多少次-(C+D)计算出有多少次A+B+C+D=0
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
//这是一个检测存在性的问题:是不是有数据满足:A+B+C+D=target,所以可以使用map
//想到了整体使用map进行判断是不是存在target-(A+B)=(C+D),
//所以需要将一部分数据存入到map中与外边的值进行判断-->将A+B的结果存入到map中使用target-(C+D)的值在map中是不是存在key,有多少次value?
//定义无序映射unordered_map,key存储遍历的A+B的值,value存储次数
unordered_map <int,int>map_num;
int res=0;
for(auto x:nums1)
{
for(auto i:nums2)
{
//方式1:使用[]
map_num[x+i]++;
//方式2:使用迭代器
// unordered_map <int,int>::iterator iter = map_num.find(x+i);
// if(iter==map_num.end())map_num.insert(pair<int,int>(x+i,1));
// else iter->second++;
}
}
//遍历C+D判断是不是等于0
for(auto i :nums3)
{
for(auto x :nums4)
{
unordered_map <int,int> ::iterator iter =map_num.find(0-(i+x));
if(iter!=map_num.end()) res +=iter->second;
}
}
return res;
}
};
赎金信
题目链接:LeetCode383、 赎金信
整体思路:
判断能不能由另一个字符串中的字符组成,存在性的问题,可以使用哈希表
方式1:数组模拟哈希表
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
//使用数组模拟哈希表
int A[26];
for(auto i :magazine)
{
A[i-'a']++;
}
for(auto i:ransomNote)
{
A[i-'a']--;
if(A[i-'a']<0)return false;
}
return true;
}
};
方式2:这个题需要记录使用的次数,可以使用unordered_map
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
//key记录出现的字符,value记录出现的次数
unordered_map <char,int>map_num;
for(auto i:magazine)
{
map_num[i] ++;
}
for(auto i :ransomNote)
{
map_num[i]--;
if(map_num[i]<0)return false;
}
return true;
}
};
思路三:可以使用multiset记录出现多次的值,需要使用erase
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
multiset <int> set_num;
for(auto i :magazine)
{
set_num.insert(i);
}
for(auto i :ransomNote)
{
multiset <int>::iterator iter =set_num.find(i);
if(iter!=set_num.end())set_num.erase(iter);//使用erase的时候,传入迭代器才能删除准确的一个元素,传入元素就会将所有的该元素删除。
else
{
return false;
}
}
return true;
}
};
三数之和
题目链接:
整体思路:
因为是在同一个数组中寻找三个元素,满足A+B+C=0:
不建议使用set、map:
1、数组中可能存在相同的值,使用set、map很难去控制重复的组合,
2、当前判断的元素与set、map中的值很难找到独立的关系
可以将一个元素固定,使用双指针遍历其他的元素,寻找A+B+C=0
步骤流程:
1、使用双指针,需要对数组进行排序,使用sort或者自己写一个简单的归并、快排
2、使用i将数组进行遍历
3、当nums[i]>0时,表示A+B+C必定大于0,可以直接退出循环了
4、将第一个指针从i+1开始循环,将第二个指针从nums.size()-1开始循环,向中间靠拢
5、A+B+C>0时,移动第二个指针j--;A+B+C<0时,移动第一个指针m++;
5、当遇到A+B+C=0的值时,记录当前的三个值 ,并且将第一个指针m向右移动m++,第二个指针j向左移动j--,但是要去掉重复元素:
1、在移动的时候,需要确保移动到的下一个值与当前的值不同
2、同时要保证左指针<右指针。
6、对i遍历,也要将重复的元素跳过(在移动的时候,需要确保移动到的下一个值与当前的值不同,)
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
//如果使用set、map会比较困难,因为需要确保三个元素各不相同。
//因为在同一个数组中,可以选择使用双指针
//定住一个位置,使用双指针将其他的位置遍历(双指针需要将进行一个排序)
vector<vector<int>>res;
sort(nums.begin(),nums.end());
//从头到尾进行遍历
for(int i=0;i<nums.size();i++)
{
//对其他位置进行双指针遍历
if(nums[i]>0) return res;//如果最小值都>=0,表示不存在三元组满足A+B+C=0;
//双指针开始遍历,需要确保m与i不相等
int m= i+1;
int j=nums.size()-1;
while(m<j)
{
if(nums[i]+nums[m]+nums[j]<0)m++;
else if(nums[i]+nums[m]+nums[j]>0)j--;
else if(nums[i]+nums[m]+nums[j]==0)
{
res.push_back({nums[i],nums[m],nums[j]});
while(m<j&&nums[m]==nums[m+1])m++;//删除相同项,必须添加m<j的限制项,不添加会导致num[m+]超出数组大小,访问越界,
while(m<j&&nums[j]==nums[j-1])j--;//删除相同项
m++,j--;//进行下一个
//同时i向下一个
while(nums[i]==nums[i+1])i++;//寻找到下一个不等于i的前一个值
}
}
}
return res;
}
};
// class Solution {
// public:
// vector<vector<int>> threeSum(vector<int>& nums) {
// sort(nums.begin(),nums.end());
// vector<vector<int>>res;
// for(int i=0;i<nums.size();i++)
// {
// int j = i+1;
// int q = nums.size()-1;
// if(nums[i]>0) return res;
// if(i>0&&nums[i]==nums[i-1])continue;
// while(j<q)
// {
// if(nums[i]+nums[j]+nums[q]<0)j++;
// else if(nums[i]+nums[j]+nums[q]>0) q--;
// else if(nums[i]+nums[j]+nums[q]==0)
// {
// res.push_back({nums[i],nums[j],nums[q]});//添加元素
// //去重
// if(j<q&&nums[j]==nums[j+1])j++;
// if(j<q&&nums[q]==nums[q-1])q--;
// j++;
// }
// }
// }
// return res;
// }
// };
四数之和
题目链接:LeetCode18、四数之和
整体思路:
与三数之和类似,通过确定两个值,其余值使用双指针进行遍历。
因为要确定两个值, 所以使用两层for嵌套循环,
在每层的循环中,需要进行减枝处理,判断当前两个确定的元素与target的关系
注意:target的值可能是复数,所以需要在满足两元素和>target的基础上再加一个限制条件:两元素和>=0,才能确保减枝的成功(这是导致出错的地方)
注意:在减枝成功后,要使用break,不能直接return res,因为内存循环break后,还会继续遍历外层的循环,只有外层的循环减枝成功才能使用return res。
注意:在使用四元素相加时,可能元素过大,强制转换成long型
注意:每层循环的最后,需要判断下一层循环开始时,确定的元素的位置(去掉重复元素)
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> res;
sort(nums.begin(),nums.end());
//三数之和是定一位,四数之和是定两位,嵌套循环
//使用i在最左,j在i的右边,双指针确定另外的两位
for(int i =0;i<nums.size();i++)
{
if(nums[i]>target&&nums[i]>=0)return res;//前两位>target,表示四位一定大于target
for(int j = i+1;j<nums.size()-1;j++)
{
if(nums[i]+nums[j]>target&&nums[i]+nums[j]>=0)break;
int m= j+1,n=nums.size()-1;
while(m<n)
{
if((long)nums[i]+nums[j]+nums[m]+nums[n]<target) m++;
else if((long)nums[i]+nums[j]+nums[m]+nums[n]>target) n--;
else if((long)nums[i]+nums[j]+nums[m]+nums[n]==target)
{
//记录合适的值、去掉m,n的重复、m<n、m,n向中间移动
res.push_back({nums[i],nums[j],nums[m],nums[n]});
while(m<n&&nums[m]==nums[m+1])m++;
while(m<n&&nums[n]==nums[n-1])n--;
m++,n--;
}
}
//最后需要确定:j下一次是不是重复的元素
while(j+1<nums.size()&&nums[j]==nums[j+1])j++;
}
//最后需要确定:i下一次是不是重复的元素
while(i+1<nums.size()&&nums[i]==nums[i+1])i++;
}
return res;
}
};