一、赎金信
1.题目
2.思路
这道题和242. 有效的字母异位词 - 力扣(LeetCode)很像。都用了数组做哈希表用来记录每个字母出现的次数。
先用哈希表记录magazine每个字母出现的次数,后面遍历ransomNote的字母时,可以一起判断数值是否小于0(小于0说明ransomNote有的字母magazine没有,可以直接退出)
3.代码
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int mark[26];
if(ransomNote.length() > magazine.length()){
return false;
}
for(int i=0;i < magazine.length();i++){
mark[magazine[i]-'a']++;
}
for(int j=0;j < ransomNote.length();j++){
mark[ransomNote[j]-'a']--;
if(mark[ransomNote[j]-'a'] < 0){
return false;
}
}
return true;
}
};
二、三数之和
1.题目
2.思路
这道题用双指针法容易理解,后续可以补上哈希表法。
题目重点是不遗漏且不重复地找到满足条件的三个数。
2.1 不遗漏
需要对数组进行重新从小到大排列,这点很重要,方便后续进行遍历。
有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。依然还是在数组中找到nums[i]+ nums[left]+ nums[right] = 0。
接下来如何移动left 和right呢, 如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。
如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。
2.2 不重复
需要对三个数遍历的过程中进行去重操作。因为对数组进行了排序,因此进行去重时,可以判断当前元素和周围的元素是否相同,相同则移动对应指针。
2.2.1 nums[i]去重操作
判断nums[i]与nums[i+1]还是与nums[i-1]相同去重呢?
答案是nums[i-1]
当判断与nums[i+1]相同时,会导致{-2,-2,4}这种结构被直接忽略。即当前元素要和遍历过的元素进行比较才能去重,与未遍历的元素去重会导致结果遗漏。当两者重复时,i 需要进入下一个循环。
2.2.2 nums[left]、nums[right]去重操作
与上面同理,nums[left]与nums[left+1]比较,nums[right]与nums[right-1]比较。
需要注意的是,left和right的去重操作需要在找到结果时才能进行操作,不然会导致{0,0,0,0,0}这种数组无法找到对应结果。
3.代码
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;//如果重新排序后的首个数字大于0,说明不可能组成=0了,直接返回结果
if(i>0 && nums[i] == nums[i-1]) continue;//对首个数字进行去重
//定义双指针
int left = i+1;
int right = nums.size()-1;
//开始寻找
while(left < right){//这里不能有=
if(nums[i]+nums[left]+nums[right] == 0){
result.push_back(vector<int>{nums[i],nums[left],nums[right]});
while(right > left && nums[right] == nums[right-1]) right--;//用while可以一直去重
while(right > left && nums[left] == nums[left+1] ) left++;
//找到答案,双指针一起移动(因为只移动一个肯定会导致重复的)
right--;
left++;
}
else if(nums[i]+nums[left]+nums[right] > 0){//组合偏大,右边的数减小
right--;
}
else{//组合偏小,左边的数增大
left++;
}
}
}
return result;
}
};
三、四数之和
1.题目
2.思路
这道题和三数之和类似,需要注意的点是:
nums[i] > target && (nums[i] >=0 || target >= 0)这个条件可以判断后面的值是否还能组成target。
后面数相加后判断要加long字符,否则可能会溢出
3.代码
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 && (nums[i] >=0 || target >= 0)) break;//如果重新排序后的首个数字大于0,说明不可能组成=0了,直接返回结果
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(left < right){//这里不能有=
if((long)nums[i]+nums[j]+nums[left]+nums[right] == target){
result.push_back(vector<int>{nums[i],nums[j],nums[left],nums[right]});
while(right > left && nums[right] == nums[right-1]) right--;//用while可以一直去重
while(right > left && nums[left] == nums[left+1] ) left++;
//找到答案,双指针一起移动(因为只移动一个肯定会导致重复的)
right--;
left++;
}
else if((long)nums[i]+nums[j]+nums[left]+nums[right] > target){//组合偏大,右边的数减小
right--;
}
else{//组合偏小,左边的数增大
left++;
}
}
}
}
return result;
}
};