1 哈希题(1)
我的第一版TLE 的做法,时间应该是 200*200*200*log(200) 。
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4)
{
// 思路: 前三个vector 还是遍历 最后一个vector 用unordered_map(猜一下 没有重复的元素)
//后面跑发现测试样例有重复的 用 multimap
int num = 0;
multimap<int,int> x;
for(int i = 0; i < nums4.size();i++)
x.insert(pair<int,int>(nums4[i],i));
for(int i = 0; i < nums1.size();i++)
for(int j = 0; j < nums2.size();j++)
for(int k = 0; k < nums3.size();k++)
{
int res = 0 - nums1[i] - nums2[j] - nums3[k];
if(x.find(res)!=x.end())
num+= x.count(res);
}
return num;
}
};
后来看了carl 讲解.....其实,这道题需要转一下脑筋,我上面的写法是遍历前三个vector 查找最后一个,其实把4个vector割分为2,2一对就可以了。先遍历前2个vector,在map存结果,之后遍历后两个vector 查询map。(注意这里的对于map比较简单的写法是——map 后一个int 应该存的是 这个key 在前两个vector的任意两数之和出现的次数!!! AC代码如下:
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4)
{
// carl的思路
unordered_map<int,int> x; // 注意这里的前一个int表示nums1和nums2任意两数和, 后一个int表示这个和的次数
for(auto i : nums1)
for(auto j : nums2)
if(x.find(i+j) != x.end())
x[i+j]++;
else x[i+j] = 1;
// for(auto iter = x.begin();iter != x.end();iter++)
// cout<<iter->first<<' '<<iter->second<<endl;
int num = 0;
for(auto i:nums3)
for(auto j:nums4)
if(x.find(0-i-j) != x.end()) num+=x[0-i-j];
return num;
}
};
2 哈希题(2)【易】
是 242.有效的字母异位词 类似的题目,简单
class Solution {
public:
bool canConstruct(string ransomNote, string magazine)
{
int x[26] = {0};
int y[26] = {0};
for(auto i:ransomNote)
x[i - 'a']++;
for(auto i:magazine)
y[i - 'a']++;
for(int i = 0; i < 26;i++)
if(x[i] > y[i])return 0;
return 1;
}
};
3 表面哈希,实则双指针题【难】
本题难点: 答案中不可以包含重复的三元组【去重】。
1. 首先一开始没什么思路,用哈希表的做法感觉很容易超时,看了提示,先sort一下再用多指针的做法===》原因是这里不用返回下标,只用返回数值 所以可以sort ==》 经验:最终结果不用下标只要数值时候可以考虑——双指针和sort的搭配!!!
于是写出下面这坨,很明显写的不完整 l,r 都没动 + 很明显没有去重【没注意看这个条件】,回头来感觉也能改好??
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums)
{
// n = 3000 * 3000
// -4 -1 -1 0 1 1 2
int l = 0;
int r = nums.size() - 1;
vector<vector<int>> x;
sort(nums.begin(),nums.end()); //升序一下
while(l < r)
{
int mid = r - 1;
int a = nums[l];
int c = nums[r];
while(mid > l)
{
int b = 0 - a - c;
if(nums[mid] != b)mid--;
else if(nums[mid] )
else
{
vector<int> tmp;
tmp.push_back(nums[l]);
tmp.push_back(nums[r]);
tmp.push_back(nums[mid]);
x.push_back(tmp);
}
}
}
return x;
}
};
.......改失败了,因为 l 和 r 两个同时做 变!量 ,在前进的过程中 l 和 r 的前进有矛盾!!!
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums)
{
// -4 -1 -1 0 1 2
int l = 0;
int r = nums.size() - 1;
vector<vector<int>> x;
sort(nums.begin(),nums.end()); //升序一下
while(l < r )
{
if(l != 0 && nums[l - 1] == nums[l])l++; //去重
if(r != nums.size() && nums[r] == nums[r + 1])r--;
int mid = r - 1;
while(mid > l)
{
int b = 0 - nums[l] - nums[r];
if(nums[mid] != b)mid--;
else
{
vector<int> tmp;
tmp.push_back(nums[l]);
tmp.push_back(nums[r]);
tmp.push_back(nums[mid]);
x.push_back(tmp);
while( mid > l && nums[mid] == nums[mid - 1])mid--;
mid--;
}
}
l++; // ?? 这里有问题 l++ r不变 还是l不变 r-- 还是l++ r-- 都矛盾
// r--;
}
return x;
}
};
换成了carl的 i + l + r 三指针的写法(首先将数组排序,然后有一层for循环,i 从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。) + 自己加了个暴力去重,但是TLE了
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums)
{
// -4 -1 -1 0 1 2
// -4 -1 -1 -1 0 1 2
// -4 -1 -1 0 1 2 2
vector<vector<int>> x;
sort(nums.begin(),nums.end());
for(int i = 0; i < nums.size();i++)
{
int l = i+1;
int r = nums.size() - 1;
while(l < r)
{
cout << i << ' ' << l << ' ' << r << endl;
if(nums[i]+nums[l]+nums[r] == 0)
{
cout << "----in" << endl;
vector<int> a;
a.push_back(nums[i]);
a.push_back(nums[l]);
a.push_back(nums[r]);
sort(a.begin(),a.end());
if(find(x.begin(),x.end(),a) == x.end()) //去重 超时了!!
x.push_back(a);
l++;
r = nums.size() - 1;
}
else if (nums[i]+nums[l]+nums[r] > 0 )
r--;
else l++;
}
}
return x;
}
};
终于AC了!!!(看了carl的视频 照着那个思路写的┭┮﹏┭┮
去重需要在正常扫的时候就完成,【1】对于i的去重,见下图
【2】对于l 和 r 的去重: 由于第一次找到nums[l] nums[r] 的时候已经收割了, 所以 l 、r 直接走到不等于当下的数值的节点即可。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums)
{
// -4 -1 -1 0 1 2
// -4 -1 -1 -1 0 1 2
// -4 -1 -1 0 1 2 2
vector<vector<int>> x;
sort(nums.begin(),nums.end());
for(int i = 0; i < nums.size();i++)
{
int l = i+1;
int r = nums.size() - 1;
if(i!=0 && nums[i] == nums[i-1])continue; // 对i去重
while(l < r)
{
int sum = nums[i]+nums[l]+nums[r];
if(sum > 0)
r--;
else if(sum < 0)
l++;
else // 收获了!!
{
vector<int> a;
a.push_back(nums[i]);
a.push_back(nums[l]);
a.push_back(nums[r]);
x.push_back(a);
// 对 l 和 r 去重
while(l < r && nums[l+1] == nums[l])l++;
while(l < r && nums[r-1] == nums[r])r--;
l++;
r--;
}
}
}
return x;
}
};
4 表面哈希,实则双指针题
和上一题三数之和 的思路一致,多一层FOR循环而已,定两个指针 i 、j ,移动 l 、 r指针。注意这里不能写当nums[i] > target就break的减枝,因为可能后面三个数相加是负数,越加越小!!如果写减枝需要写成下面这样,我的代码里没有写减枝。
if (nums[k] > target && nums[k] >= 0)
if (nums[k] + nums[i] > target && nums[k] + nums[i] >= 0)
没看提示,自己写完的AC代码:
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target)
{
// 按照三数之和的思路写一个
sort(nums.begin(),nums.end());
vector<vector<int>> out;
if(nums.size() < 4)return out; // 注意 size 为1,2,3 的特判!
for(int i = 0; i < nums.size() - 3;i++)
{
if(i != 0 && nums[i-1] == nums[i])continue; //对于i去重
for(int j = i + 1; j < nums.size() - 2;j++)
{
if(j != i+1 &&nums[j-1] == nums[j])continue; //对于j去重
int l = j + 1;
int r = nums.size() - 1;
// cout << i << ' ' << j << ' ' << l << ' ' << r << endl;
while(l < r)
{
if((long long)nums[i] + nums[j] + nums[l] + nums[r] > target)r--; //这里要开longlong
else if((long long)nums[i] + nums[j] + nums[l] + nums[r] < target)l++;
else //相同了
{
out.push_back({nums[i],nums[j],nums[l],nums[r]});
while(l < r && nums[l + 1] == nums[l])l++; //去重
while(l < r && nums[r - 1] == nums[r])r--;
l++;
r--;
}
}
}
}
return out;
}
};
感受:
1. 第3题 三数之和写的比较麻,写了4版才AC。其他还好
2. 跟上进度当天写的,总共用时3h45min。