四数相加
1.问题描述
给定四个包含整数的数组列表 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 。所有整数的范围在 -228 到 228 - 1 之间,最终结果不会超过 231 - 1 。
例如:
输入:
A = [ 1, 2]
B = [-2,-1]
C = [-1, 2]
D = [ 0, 2]
输出:
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
2.求解
哈希表
-
看到形如:A+B....+N=0的式子,要转换为(A+...T)=-((T+1)...+N)再计算,这个T的分割点一般是一半,特殊情况下需要自行判断。
为什么一般情况分割点是一半,例如本题的四个元组,分割方法有以下三种
- HashMap 存一个数组,如 A。然后计算三个数组之和,如 BCD。时间复杂度为:
O(n)+O(n^3),得到O(n^3). - HashMap 存三个数组之和,如 ABC。然后计算一个数组,如 D。时间复杂度为:
O(n^3)+O(n),得到O(n^3). - HashMap存两个数组之和,如AB。然后计算两个数组之和,如 CD。时间复杂度为:
O(n^2)+O(n^2),得到O(n^2).
因此,分割一半是最优事件复杂度。
- HashMap 存一个数组,如 A。然后计算三个数组之和,如 BCD。时间复杂度为:
-
在本题中,可以先将两个元组的各个元素相加的和存入hash表中,再遍历另外两个元组,看能否找到等于0的值
代码如下
/*
执行用时:69 ms, 在所有 Java 提交中击败了89.83% 的用户
内存消耗:56.6 MB, 在所有 Java 提交中击败了92.34% 的用户
*/
public int fourSumCount(int[] A, int[] B, int[] C, int[] D) {
Map<Integer, Integer> map = new HashMap<>();
for(int x : A){
for(int y : B){
map.put(x + y, map.getOrDefault(x + y, 0) + 1);
}
}
int ans = 0;
for(int x : C){
for(int y : D){
if(map.containsKey(-x -y)){
ans += map.get(-x -y);
}
}
}
return ans;
}
- 时间、空间复杂度均为O(n^2)
关于HashMap容量:
-
可以在创建HashMap时传入初始桶大小,例如在本题中可以创建大小为
A.length * A.length的的HashMap。这样可以避免因扩容而带来的性能损耗。Map<Integer, Integer> map = new HashMap<>(A.length * A.length); -
在创建HashMap时不传入初始桶大小,则默认桶大小为16
-
java 7中的扩容机制需满足两个条件
- 存放新值的时候当前已有元素的个数必须大于等于阈值
- 存放新值的时候当前存放数据发生hash碰撞(当前key计算的hash值换算出来的数组下标位置已经存在值)
因此可能会出现以下情况
(1)、就是hashmap在存值的时候(默认大小为16,负载因子0.75,阈值12),可能达到最后存满16个值的时候,再存入第17个值才会发生扩容现象,因为前16个值,每个值在底层数组中分别占据一个位置,并没有发生hash碰撞。
(2)、当然也有可能存储更多值(超多16个值,最多可以存26个值)都还没有扩容。原理:前11个值全部hash碰撞,存到数组的同一个位置(虽然hash冲突,但是这时元素个数小于阈值12,并没有同时满足扩容的两个条件。所以不会扩容),后面所有存入的15个值全部分散到数组剩下的15个位置(这时元素个数大于等于阈值,但是每次存入的元素并没有发生hash碰撞,也没有同时满足扩容的两个条件,所以叶不会扩容),前面11+15=26,所以在存入第27个值的时候才同时满足上面两个条件,这时候才会发生扩容现象。
-
java 8的扩容机制
(1)Java 8 在新增数据存入成功后进行扩容
(2)扩容会发生在两种情况下(满足任意一种条件即发生扩容):
a 当前存入数据大于阈值即发生扩容
b 存入数据到某一条链表上,此时数据大于8,且总数量小于64即发生扩容
(3)此外需要注意一点java7是在存入数据前进行判断是否扩容,而java8是在存入数据库在进行扩容的判断。

浙公网安备 33010602011771号