四数相加II

力扣题目链接(opens new window)

给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。

为了使问题简单化,所有的 A, B, C, D 具有相同的长度 N,且 0 ≤ N ≤ 500 。所有整数的范围在 -2^28 到 2^28 - 1 之间,最终结果不会超过 2^31 - 1 。

例如:

输入:

  • A = [ 1, 2]
  • B = [-2,-1]
  • C = [-1, 2]
  • D = [ 0, 2]

输出:

2

解释:

两个元组如下:

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

初见分析

题目给我们提供了nums1~4四个长度相同输入数组,我们需要在每个数组中选择一个下标,组成一个含4个元素的元组

这四个下标对应的数组中的元素相加需要正好为0

似乎没有能够使用哈希表的点

存下标吗?下标元组按题意来算有n的四次方种

也不太可能用暴力法,那样需要四个循环,复杂度为n的四次方

思路

先声明,本题就是哈希法的经典题型。如何想到用哈希表是本题的关键。先梳理思路吧

由题意,我们需要在四个数组中找出相加等于零的情况出现的次数

注意,不需要给出对应的元素,只要记录等于零这种情况出现的次数

一种自然的想法是:

int count = 0;
for(a:A){
    for(b:B){
        for(c:C){
            for(d:D){
                int sum = a + b + c + d;
                if(sum ==0){
                    count ++;
                }
            }
        }
    }
}

时间复杂度n * n * n * n,不太行

反正肯定是要遍历数组的,能不能分开遍历?

于是,可以将AB分为一组,CD分为另一组

而且,肯定不能同时遍历这两组,要不然还是n * n * n * n

所以得分开计算遍历到的值的和

题目的要求是

nums1[i]+nums2[i]+nums3[i]+nums4[i] = 0

设A遍历到的值为a,以此类推有b、c、d

那么:

a+b+c+d = 0

先遍历AB数组,计算a+b,存入一个集合

这里有一种情况需要注意:

例如所给的数组中元素全为0,那么不同的0相加也算是不同的一种情况

所以计算a+b时还要记录其出现过的次数

因为需要记录两个值,所以使用map比较合适

然后遍历CD数组,计算c+d

那么这里就有使用哈希表的地方了,参考两数之和的处理方式

我们可以去集合中查询一个 target ,target = 0-(c+d)

如果 target 在集合中存在,那么就找到了一个满足条件的情况

不难看出,这里的target就是a+b的值

代码

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int, int> map;//创建一个map作为哈希表
        //遍历前两个数组A、B
        for(int n1 : nums1){//也可以写auto
            for(int n2 : nums2){
                // int sum = n1 + n2;
                map[n1 + n2]++;//在map中的sum位置标记1
            }
        }

        int count = 0;
        //遍历剩下的数组CD
        for(int n3 : nums3){
            for(int n4 : nums4){
                int sum = n3 + n4;
                int target = 0 - sum; 
                auto iter = map.find(target);
                if(iter != map.end()){//找到target
                    count += map[target]; 
                }
            }
        }
        return count;
    }
};
注意点
1、遍历vector

可以使用c++中迭代器的风格去遍历,即:

for(int n1 : nums1){//也可以写auto,让编译器自己去判断类型,当然题目已经给了vector的类型就直接写int
   for(int n2 : nums2){
       // int sum = n1 + n2;
       map[n1 + n2]++;//在map中的sum位置标记1
   }
}

也可以传统一点(实际上这样也是用迭代器)

for (it = nums1.begin(); it != nums1.end();it++) {
	cout << *it << " ";
}
2、map插入数据
#include <map>

void printMap(map<int,int>&m)
{
	for (map<int, int>::iterator it = m.begin(); it != m.end(); it++)
	{
		cout << "key = " << it->first << " value = " << it->second << endl;
	}
	cout << endl;
}

void test01()
{
	//插入
	map<int, int> m;
	//第一种插入方式
	m.insert(pair<int, int>(1, 10));
	//第二种插入方式
	m.insert(make_pair(2, 20));
	//第三种插入方式
	m.insert(map<int, int>::value_type(3, 30));
	//第四种插入方式
	m[4] = 40; 
	printMap(m);

	//删除
	m.erase(m.begin());
	printMap(m);

	m.erase(3);
	printMap(m);

	//清空
	m.erase(m.begin(),m.end());
	m.clear();
	printMap(m);
}

int main() {

	test01();

	system("pause");
	return 0;
}

就用第四中就行,把map当成python中的字典