Leetcode 454 四数相加 II
题目链接 454 四数相加 II
本题目主要考察的还是哈希表中的unordered_map,为什么用map,是因为四个数组没有限制范围,其次需要记录下标和出现的次数,所以只能用map,为什么要用unordered_map,因为本题目对key没有要求有序性,我们将从中选择效率最高的那个,所以只能选择unordered_map。将四个数分成两两相加,这样是时间复杂度最小的情况,下面直接上代码:
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int ,int> umap;//先建立unordered_map:对key无要求有序,且效率高
int cnt = 0;
for(int a: nums1){
for(int b:nums2){
umap[a+b]++;//统计次数,同一结果不一定是相同元素带来的
}
}
for(int c:nums3){
for(int d:nums4){
int target = 0-(c+d);//寻找和值为零的目标值
if(umap.find(target)!=umap.end()){//若找到
cnt+=umap[target];//加上次数即是答案,原理就是组合数
}
}
}
return cnt;
}
};
Leetcode 383 赎金信
题目链接383 赎金信
本题目和前面做过的 有效的字母异位词 很相似,先把magazine中出现过的元素记录下来,这里对record[magazine]的做加法,然后去对比magazine中的元素,对比的过程就是对record[ransomNote]做减法,若record[magazine]= =record[ransomNote],record[ransomNote]就为0,返回true,否则就为负数,为负数就返回false,还有,ransomNote长度>magazine长度
那就返回false
下面上代码和注释:
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int record[26] = {0};
if(ransomNote.size()>magazine.size()){
return false;`//ransomNote 不能构成 magazine 里面的字符
}
for(int i=0;i<magazine.length();i++){
record[magazine[i]-'a']++;
// 通过record数据记录 magazine里各个字符出现次数
}
for(int j=0;j<ransomNote.length();j++){
record[ransomNote[j]-'a']--;
// 遍历ransomNote,在record里对应的字符个数做--操作
if(record[ransomNote[j]-'a']<0){// 如果小于零说明ransomNote里出现的字符,magazine没有
return false;
}
}
return true;
}
};
Leetcode 15 三数之和
题目链接 15 三数之和
本题目实际上有两种方法,一种是哈希法(map),但是考虑去重的地方太多了,所以就不再考虑这个方法,而改用双指针法,先说一下简单的思路,首先对数组中的元素进行一个排序,接着要固定好指针的位置(位置如图所示)
排序后若nums[i]<0,就不存在三数之和大于0的情况了,除去这种情况,就是正常的双指针移动了,i首先是正常移动,如果三个数之和小于0,就让left右移,使之和增大,如果反之,就让right左移,让之和减小,直到三数之和等于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;//排序后,数组开头元素大于零,就没无法满足条件
}
//明确一点,a动一步之后,left和right不停的移动寻找满足题意的值
//这里是去重的第一步操作,若i指针指向的元素和之前的i指针指向的元素相同,即重复a;
//还有一种错误的去重操作:
/*
if (nums[i] == nums[i + 1]) {
continue;
}
*/
//本方法就遗漏了-1,-1,2这种情况
//我们要做的是 不能有重复的三元组,但三元组内的元素是可以重复的。
if(i>0&&nums[i]==nums[i-1]){
continue;//所以这里要保证数组下标始终大于0
}
int left = i+1;//双指针定位
int right = nums.size()-1;
while(right>left){
if(nums[i]+nums[right]+nums[left]<0){
left++;//如果小于0,就增大三数之和
}else if(nums[i]+nums[right]+nums[left]>0){
right--;//如果大于0,就减小三数之和
}else{//如果等于就记录到result上面
result.push_back(vector<int>{nums[i],nums[left],nums[right]});
//函数将一个新的元素加到vector的最后面,位置为当前最后一个元素的下一个元素,push_back() 在Vector最后添加一个元素(参数为要插入的值)
//第二步去重
while(right>left&&nums[right]==nums[right-1]){
right--;//如果right和right前一位数组重复就去重
}
while(right>left&&nums[left]==nums[left+1]){
left++;//同理
}
right--; //找到答案,不停的收缩双指针,继续找
left++;
}
}
}
return result;//返回result
}
};
Leetcode18 四数之和
题目链接 18 四数之和
本题目和三数之和的思路是一模一样的,都是运行双指针法,只不过在三数之和的基础上加上了一层for循环,来控制第四个数。
下面直接上代码加注释:
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]>target&&nums[k]>0&&target>0){//剪枝,由于target不确定(可能为负数),而只有在大于零时才能剪枝
break;
}
if(k>0&&nums[k]==nums[k-1]){//去重操作,前面三数之和解释过了
continue;
}
for(int i=k+1;i<nums.size();i++ ){//这里i要在k的下一位
if(nums[k]+nums[i]>target&&nums[k]+nums[i]>0&&target>0){
break;//剪枝,和k指针一样的情况
}
if(i>k+1&&nums[i]==nums[i-1]){//去重操作
continue;
}
int left = i+1;//定义两指针位置
int right = nums.size()-1;
while(right>left){
//这里唯一的不同是
//多加一个long
//原因: nums[k] + nums[i] + nums[left] + nums[right] > target 会溢出
if((long)nums[k]+nums[i]+nums[left]+nums[right]>target){
right--;
}else if((long)nums[k]+nums[i]+nums[left]+nums[right]<target){
left++;
}else{
result.push_back(vector<int>{nums[k],nums[i],nums[left],nums[right]});
while(right>left&&nums[right]==nums[right-1]){
right--;
}
while(right>left&&nums[left]==nums[left+1]){
left++;
}
right--;//找到target,双指针不停收缩
left++;
}
}
}
}
return result;
}
};
end