Loading

四数相加

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

解释:
两个元组如下:

  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

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).

    因此,分割一半是最优事件复杂度。

  • 在本题中,可以先将两个元组的各个元素相加的和存入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中的扩容机制需满足两个条件

    1. 存放新值的时候当前已有元素的个数必须大于等于阈值
    2. 存放新值的时候当前存放数据发生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是在存入数据库在进行扩容的判断。

posted @ 2020-11-27 15:14  水纸杯  阅读(104)  评论(0)    收藏  举报