1.题目描述
给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:0 <= i, j, k, l < n
;
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
示例1:
输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
输出:2
解释两个元组如下:
(0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
(1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0
示例2:
输入:nums1 = [0], nums2 = [0], nums3 = [0], nums4 = [0]
输出:1
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/4sum-ii
2.题目分析
- 注意题目描述中的返回值。是不需要我们记录下标值或者返回元组的,所以我们就只需要考虑满足加和为0的元组个数。
我们观察题目,正常思考,还是首先以穷举的思路进行思考,但是穷举好像貌似有点艰难,毕竟是四重循环,正常的coder应该不敢这么刚,所以我们再考虑能否对这四层循环进行一些压缩减少。但是无论怎么思考,类似四次的加和操作是不能避免的,那么我们还能否降重呢?
答案是肯定的,我们类比求两数之和a+b=0
的过程,其中是对每个a
进行存储,然后查找是否有-a
。这里我们借鉴一下,我们可以考虑采用拆分的思想,将四个数组分开处理,每两个数组合并为一个数组,然后对合并后的两个数组进行求两数之和的操作,这样循环貌似就降到了两重。
这里的两个长度为n数组nums1
和nums2
的合并之后就变成了一个n*n的大数组nums
,如果我们对两个数组进行全部存储的话貌似也有点浪费空间,所以我们依然借鉴求两数之和的思路,只存储其中两个数组nums1
和nums2
构成的组合nums
,然后直接穷举处理另外两个数组nums3
和nums4
看其组成的元组相加的和是否能与nums
中的元素相加为0.
基本解题框架已经出来了,我们再考虑以下能否优化一点,我们看到上面的给出的解题过程在数组的分组合并之后,后面两个数组的操作的时候需要频繁的查找符合条件的元素,所以我们应该使用一种高效的数据结构,这时我们想到基于哈希的set
或者map
。这时应该选择哪个呢?我们知道这两个集合都是不允许重复的,所以我们如果选择set进行保存nums1
和nums2
的元素之和的话,那么和的个数就无法确定,所以我们选择map
来进行辅助存储,我们将其的k-v
对定义为两数之和-该和出现次数
。这样我们在后两个数组nums3
和nums4
的遍历时就可以累加元组数目。
具体解题步骤如下:
- 首先将四个数组分为两个部分,
nums1
和nums2
为一组,nums3
和nums4
为一组 - 初始化计数值
sum
,初始化unordered_map
保存数组nums1
和nums2
的所有加和的值与对应的数量 - 对
nums3
和nums4
遍历,若有nums3[k]+nums4[l]
的值的相反数存在于unordered_map
,则更新sum,sum +=-(nums3[k]+nums4[l])
的出现次数; - 循环完成后返回
sum
3.题目解答
哈希集合解法
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
int n= nums1.size();
//初始化返回值与辅助哈希集合
int num = 0;
unordered_map<int,int> imap;
//将前两个数组的所有情况下的相加之和作为一个map存储
//其中k-v对的含义是:两数之和-该和出现次数
for(int i = 0;i<n;i++){
for(int j = 0;j<n;j++){
imap[nums1[i]+nums2[j]]++;
}
}
/*查看后两个数组的所有情况下的相加之和
是否可以与前面两个数组的和相加为0*/
for(int k = 0;k<n;k++){
for(int l = 0;l<n;l++){
if(imap.find(0-(nums3[k]+nums4[l])) != imap.end()){
num+=imap[0-(nums3[k]+nums4[l])];
}
}
}
return num;
}
};
总结:问题需要的返回值有时可以决定难易程度。一般问题的解决都是从最简单的思路开始进行优化,如拆分合并双指针等思路。