题目链接:454. 四数相加 II
class Solution
{
public:
int fourSumCount(vector<int> &nums1, vector<int> &nums2, vector<int> &nums3, vector<int> &nums4)
{
unordered_map<int,int> record;
int count=0;
for(int i:nums1){
for(int j:nums2){
record[i+j]++;
}
}
for(int i:nums3){
for(int j:nums4){
if(record.find(0-(i+j))!=record.end()){
count+=record[0-(i+j)];
}
}
}
return count;
}
};
这道题的关键在于灵活理解,之前做过一道题,是两个数组相加求0的。这道题和那一道题异曲同工,只是数组的数量由2个变为4个,道理还是一样的。我们把每两个数组相加当作一个数组就行。以后遇到这种题目,都要会灵活理解,无论多少个数组,在相加操作后,一般都可以变换为2个数组,然后利用哈希表解决。
题目链接:383. 赎金信
class Solution
{
public:
bool canConstruct(string ransomNote, string magazine)
{
unordered_map<char,int> record;
for(char a:magazine){
record[a]++;
}
for(char b:ransomNote){
if(record[b]){
record[b]--;
}else return false;
}
return true;
}
};
这道题很简单,利用哈希表统计字母的出现次数就行,在这里我碰到一个问题,原来就算value=0,用map.find()!=map.end()也是可以在map里面找得到该函数的。第二就是用map.coumt()函数的返回值不是计数的值,而是1或0,找得到就1,找不到就0;在value=0的情况下还是找得到,所以就算value=0,count的返回值还是1。
这道题我犯了一个错误,但是leecode还是通过了,不是很清楚什么原因,我忽略了字符串为空的情况。现在细想,好像误打误撞写对了?如果字符串数组为空,那么直接跳过for循环,直接到了最下面的return true了。下面是代码随想录的准确解答:
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
for (int i = 0; i < magazine.length(); i++) {
for (int j = 0; j < ransomNote.length(); j++) {
// 在ransomNote中找到和magazine相同的字符
if (magazine[i] == ransomNote[j]) {
ransomNote.erase(ransomNote.begin() + j); // ransomNote删除这个字符
break;
}
}
}
// 如果ransomNote为空,则说明magazine的字符可以组成ransomNote
if (ransomNote.length() == 0) {
return true;
}
return false;
}
};
题目链接:15. 三数之和
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> arrays;
sort(nums.begin(),nums.end());//对nums进行快速排序
for(int i=0;i<nums.size();i++){//i是三元组的第一个元素,循环的每一轮都是找一个新的三元组
if(nums[i]>0) return arrays;//如果第一个元素都大于0,那么加上后面的也不可能等于0
//第一个元素是遍历数组获得,第二第三个元素利用双指针得出
if(i>0&&nums[i]==nums[i-1]){
//去重操作,元组里面的元素可以相等,但是元组是不可以相等的,所以要把第一个元素相等的情况去掉
continue;
}
int left=i+1;//这时候开始双指针法,left指针为第一个元素+1
int right=nums.size()-1;//right指针一开始指向数组的末端,即排序后最大的那个数
while(right>left){//右指针要大于左指针,循环在两指针相遇,即遍历完数组的时候结束
if(nums[i]+nums[left]+nums[right]>0){
right--;//结果大于0,说明结果需要减小,因为左指针一开始等于i+1,不可以减小,那么减小右指针
}else if(nums[i]+nums[left]+nums[right]<0){
left++;//结果小于0,说明结果需要增大,因为右指针一开始等于最大值,不可增大,那么减小左指针
}else{//相加结果等于0,即算出正确的三元组
arrays.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 arrays;
}
};
做后感:总之就是难,想不出来一点。一开始以为是老套路,想通过哈希法来解决,没想到不知道怎么去除相同的元素。然后看了解析发现果然不是我想的那样简单。最后选择了双指针法解题,很巧妙的方法,虽然要找三个元素,但是通过固定一个首元素,然后在第二层循环里利用双指针法同时对剩下两个值进行操作,就能做到用双重循环找三个数。这道题去重是另一个难点。时间复杂度是o(n^2)
代码随想录的解析,真的很到位,看完之后直接悟了:代码随想录 (programmercarl.com)
题目链接:18. 四数之和
class Solution
{
public:
vector<vector<int>> fourSum(vector<int> &nums, int target)
{
vector<vector<int>> arrays;
sort(nums.begin(),nums.end());
for(int i=0;i<nums.size();i++){
if(nums[i]>target&&nums[i]>0) break;
if(i>0&&nums[i]==nums[i-1]) continue;
for(int j=i+1;j<nums.size();j++){
if(nums[i]+nums[j]>target&&nums[i]+nums[j]>0) break;
if(j>i+1&&nums[j]==nums[j-1]) continue;
int left=j+1;
int right=nums.size()-1;
while(right>left){
//基本和leecode 15.三数相和一样,不同点在于这里相加可能会有溢出
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{
arrays.push_back(vector<int>{nums[i],nums[j],nums[left],nums[right]});
while(right>left&&nums[right]==nums[right-1]) right--;
while(right>left&&nums[left]==nums[left+1]) left++;
left++;
right--;
}
}
}
}
return arrays;
}
};
做题方法和leecode 15.三数之和一模一样,通过双指针来去掉一层循环,实现时间复杂度下降一个指数级别,从o(n^4)变成o(n^3),唯一要注意的是,本题相加的结果在leecode上会溢出,要在相加式子前加上(long)来强制类型转换成长整型。