代码随想录训练营day9|第454题.四数相加II,383. 赎金信,第15题. 三数之和,

第454题.四数相加II

题目链接:第454题.四数相加II
题目描述:给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。

输入:                                输出:  2

A = [ 1, 2]
B = [-2,-1]
C = [-1, 2]
D = [ 0, 2]
解释:

两个元组如下:

(0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
(1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0

思路

本题是四个独立的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不用考虑有重复的四个元素相加等于0的情况,所以相对于题目18. 四数之和,题目15.三数之和,还是简单了不少!
1. 使用哈希表里的unordered-map,key放a和b两数之和,value 放a和b两数之和出现的次数。
2. 遍历大A和大B数组,统计两个数组元素之和,和出现的次数,放到map中
3. 定义int变量count,用来统计 a+b+c+d = 0 出现的次数。
4. 再遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就用count把map中key对应的value也就是出现次数统计出来。

代码展示

//四数相加Ⅱ
与四数之和不一样
class Solution {
public:
    int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D){
    	unordered_map(int , int )map;
		for(a:A){//遍历前两个数组去统计a+b
		 for(b:B) {
		 	map[a+b]++;//用map去映射,并且value加一 
		 }    
		}
	// 在遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就把map中key对应的value也就是出现次数统计出来。 
		for (int c : C) {
            for (int d : D) {
            	target = 0 - (c+d);//这就是要查的目标值,因为加起来才为0 
                if (umap.find(0 - (c + d)) != umap.end()) {、、//在遍历过程中找到target 
                    count += map[target];//这里的value出现的次数就是符合条件按的键值数,key即对应的数值 
                }
				}
				}
		return count;		
	} }

383. 赎金信

题目链接:383. 赎金信
题目描述:给定一个赎金信 (ransom) 字符串和一个杂志(magazine)字符串,判断第一个字符串 ransom 能不能由第二个字符串 magazines 里面的字符构成。如果可以构成,返回 true ;否则返回 false。

你可以假设两个字符串均只含有小写字母。

canConstruct("a", "b") -> false
canConstruct("aa", "ab") -> false
canConstruct("aa", "aab") -> true

思路

直接暴力解法--两层for循环,不断去查找。代码如下:

暴力解法:
class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
		for(i = 0; i < magazine.size(); i++){//bian li A
			for(j = 0; j < ransonNote.size(); j++){//bianliB
				if(ma[i] == ran[j]){//在遍历ran里找到和ma数组的相同的字符 
					ran.erase(ran.begin() + j);//删除掉相同的字符
					break; 
			}
		}
		}
		if(ran.size() = 0){//如果ran数组为空,那么ma的字符就直接组成ran 
			return true;
		}
		return false; 
	}
}

哈希解法

依然是数组在哈希法中的应用。
那么为什么用数组你???用数组干啥,都用map完事了,其实在本题的情况下,使用map的空间消耗要比数组大一些的,因为map要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效!

代码如下

//哈希法解赎金
题目所只有小写字母,那可以采用空间换取时间的哈希
策略, 用一个长度为26的数组还记录magazine里字母出现的次数 
再用ransomNote去验证这个数组是否包含了ransomNote所需要的所有字母
class Solution {
public:
    bool canConstruct(string ransomNote, string magazine){
    	int record[26];
		if(ran.size( > mag.size())){//ran数组若大于mag数组,直接gg 
			return false;
		}	
		if(int i = 0; i < mag.seze(); i++ ){
			record[magazine[i]-'a'] ++;// 通过recode数据记录 magazine里各个字符出现次数
		} 
		for(int j = 0; j < ran.size(); j++){
			record[ransomNote[j]-'a']--;// 遍历ransomNote,在record里对应的字符个数做--操作
			if(record[ran[j] - 'a'] < 0){ 
				return false;// 如果小于零说明ransomNote里出现的字符,magazine没有
			}
		}
		return true;
	}
	}

15.三数之和

题目链接:15.三数之和
题目描述:给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请
你返回所有和为 0 且不重复的三元组。

示例:

给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ]

思路

使用哈希法非常棘手,去重的过程不好处理,有很多小细节,如果在面试中很难想到位。
时间复杂度可以做到O(n^2),但还是比较费时的,因为不好做剪枝操作。
双指针法

拿这个nums数组来举例,首先将数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。

依然还是在数组中找到 abc 使得a + b +c =0,我们这里相当于 a = nums[i],b = nums[left],c = nums[right]。

接下来如何移动left 和right呢, 如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。

如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。

时间复杂度:O(n^2)。

代码实现

//第15题. 三数之和
判断 nums 中是否存在三个元素 a,b,c ,使得
 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
 class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
    	vector<vector<int>> result;	//定义一个二维数组
		sort(nums);从小到大进行一个排序
		for(i = 0; i < nums.size(); i++){//i开始从第一个位置遍历数组
			if(nums[i] > 0){
				return false;//如果第一个数就大于0 后面的b加c 就不能为=0了 
			}	
			if(i > 0 && nums[i] ==nums[i - 1]) {
				continue;//这里就是进行对i的去重操作,注意是 nums[i] ==nums[i - 1]才能跳过进入下一次循环
				 
			}
			left = i + 1;
			right = nums.size() - 1;
			while(right > left){//若rig = left 说明指向同一个元素,则没有满足abc三个数,不能行 
				if (nums[i] + nums[left] + nums[right] > 0) right--;//移动指针 
                else if (nums[i] + nums[left] + nums[right] < 0) left++;//移动指针 
                else{//即等于0时的情况 
                	result.push(vector<int>{nums[i], nums[left], nums[right]});//收获三元组结果 
					// 去重逻辑应该放在找到一个三元组之后,对b 和 c去重
                    while (right > left && nums[right] == nums[right - 1]) right--;//right和前一位相等则 左移 
                    while (right > left && nums[left] == nums[left + 1]) left++; //left和后一位相等 右移
					// 收获结果时,双指针同时收缩
                    right--;
                    left++; 
				}
			}
	}
	return result; 
	}
}
posted @   认真小l  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示