1.四数相加Ⅱ(力扣)
暴力解法四层for循环,复杂度太高辣,直接给你来个超出时间限制,所以要想办法降低时间复杂度。
4个不行,那可以抓两个,把四个数组分成两组,对第一组进行求和,用一个unordered_map来储存数及它的出现次数。用两层for循环对另外两个数组进行遍历,如果在map中找到了-(c+d)则证明组合是可行的,但是由于出现的数目可能不为1,不能直接++,需要加上map[-(c+d)]的值才是正确答案。
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int,int>init_map;
for(int a:nums1)
{
for(int b:nums2)
{
init_map[a+b]++;
}
}
int count=0;
for(int c:nums3)
{
for(int d:nums4)
{
if(init_map.find(-(c+d))!=init_map.end())
{
count+=init_map[-(c+d)];
}
}
}
return count;
}
};
2.赎金信(力扣)
对于给定的字符串magazine,里面的字符只能被ransomNote使用一次,很容易想到的就是遍历magazine,把字符串的数目进行统计用一个数组进行储存,在遍历ransomNote,出现的字符直接在数组的对应位置进行取出,如果某一个数组位置的数小于0,就证明magazine'已经满足不了ransomNote的需求了,直接return false。
P.S 在开始之前可以先比较两个字符串的长度,如果magazine更短直接return false。
bool canConstruct(char * ransomNote, char * magazine){
int result[26]={0};
int length=strlen(magazine);
for(int i=0;i<length;i++)
{
result[magazine[i]-'a']++;
}
for(int i=0;i<strlen(ransomNote);i++)
{
result[ransomNote[i]-'a']--;
if(result[ransomNote[i]-'a']<0)
{
return false;
}
}
return true;
}
3.三数之和(力扣)
相比起前两道这个题会更加复杂一点,而复杂的地方在于去重+剪枝。先将数组进行排序,利用双指针进行遍历,由于要求三数之和为0的组合,当第一个数字为正数的时候,后面的其实已经不用判断了,三数之和只能大于0,此过程称为剪枝;而数组中是极有可能出现相同元素的,当题目要求不能有重复的三元组出现,那么在每一次判断前都要进行去重:
if(i>=0&&nums[i]==nums[i-1])
为什么这里是nums[i]和nums[i-1]而不是nums[i]与nums[i+1]捏?如果为后者,在双指针赋初值的时候left=i+1,这个判断条件只能限制三元组里不能有重复的元素而不是限制重复的三元组出现。
用双指针对三数之和进行判断。left指向当前位置的下一个元素,right指向最后一个元素,如果和大于0,则证明过大,所有将右指针向左移动一位(right--);如果和小于0,则证明过小,将左指针向右移动一位(left++)。如果刚好为0,那么对其进行储存,接下来去重操作。(双指针有效降低复杂度)
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++)
{
if(nums[i]>0)
{
return result;
}
if(i>=1&&nums[i]==nums[i-1])
{
continue;
}
int left=i+1;
int right=nums.size()-1;
while(left<right)
{
if(nums[i]+nums[right]+nums[left]<0)
{
left++;
}
else if(nums[i]+nums[right]+nums[left]>0)
{
right--;
}
else if(nums[i]+nums[right]+nums[left]==0)
{
result.push_back(vector<int>{nums[i],nums[left],nums[right]});
while(left<right&&nums[right]==nums[right-1])
{
right--;
}
while(left<right&&nums[left]==nums[left+1])
{
left++;
}
right--;
left++;
}
}
}
return result;
}
};
4.四数之和(力扣)
四数之和本质上就只是在三数之和的基础上加了一层for循环,四个数->固定一个,移动第二个的时候又固定,剩下两个数变成双指针移动的区间。值得注意的是,新增的一层for循环依然需要增加剪枝和去重操作,但是条件与三数之和有所不同,给定的target不再是0,所以不能仅仅依据排序后的数组出现nums[k]>target就直接宣布死刑,当出现[-3,-3,-2,0]和target=-8的时候这么做直接G。所以需要加上一点点条件,对于最外层的for循环nums[k]>target>=0,对于第二层循环nums[i]+nums[k]>target>=0,就能够顺利完成剪枝操作,而去重操作与第三题三数之和相同,不再进行赘述。
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++)
{
if(nums[k]>0&&nums[k]>target)
{
break;
}
if(k>=1&&nums[k]==nums[k-1])
{
continue;
}
for(int i=k+1;i<nums.size();i++)
{
if((long)nums[i]+(long)nums[k]>target&&nums[i]+nums[k]>=0)
{
break;
}
if(i>k+1&&nums[i]==nums[i-1])
{
continue;
}
int left=i+1;
int right=nums.size()-1;
while(left<right)
{
if((long)nums[k]+(long)nums[i]+nums[left]+nums[right]>target)
{
right--;
}
else if((long)nums[k]+(long)nums[i]+nums[left]+nums[right]<target)
{
left++;
}
else
{
result.push_back(vector<int>{nums[k],nums[i],nums[left],nums[right]});
while(left<right&&nums[left]==nums[left+1])
{
left++;
}
while(left<right&&nums[right]==nums[right-1])
{
right--;
}
left++;
right--;
}
}
}
}
return result;
}
};