如何提高Leetcode 4sum-ii挑战的性能

时间:2016-11-21 09:26:14

标签: c# algorithm search

这是一个leet代码竞赛question我试图在比赛结束后尝试但我的代码总是超出时间限制。 问题是

  

给定四个列表A,B,C,D的整数值,计算多少元组   (i,j,k,l)A [i] + B [j] + C [k] + D [l]为零。

     

为了使问题更容易,所有A,B,C,D   具有相同的N长度,其中0≤N≤500   所有整数均在-2 28 至2 28 -1的范围内   结果保证最多为2 31 -1。

示例:

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

Output:
2

Explanation:
The two tuples are:
 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

我的代码是

public static int FourSumCount(int[] A, int[] B, int[] C, int[] D)
    {
        int count = 0;
        List<int> map1 = new List<int>();
        List<int> map2 = new List<int>();

        for (int i = 0; i < A.Length; i++)
            for (int y = 0; y < B.Length; y++)
            {
                map1.Add(A[i] + B[y]);
                map2.Add(C[i] + D[y]);
            }
        for (int i = 0; i < map2.Count(); i++)
        {
            for (int j = 0; j < map2.Count(); j++)
                //if (map1.Contains(map2[i]*-1))
                //{
                //    var newList = map1.FindAll(s => s.Equals(map2[i] * -1));
                //    count = count + newList.Count();
                //}
                if (map1[i] + map2[j] == 0)
                {
                    count++;
                }
        }


        return count;
    }

有没有更好的方法?感谢您的期待。

2 个答案:

答案 0 :(得分:4)

我建议在中间算法中遇到:

 A[i] + B[j] + C[k] + D[l] = 0

实际上意味着找出A[i] + B[j]C[k] + D[l]这样

 (A[i] + B[j]) == (-C[k] - D[l])

我们可以将所有可能的A[i] + B[j]总和放入词典,然后在-C[k] - D[l]的循环中尝试查找此词典。您可以像这样实现它:

  private static int FourSumCount(int[] A, int[] B, int[] C, int[] D) {
    // left part: all A[i] + B[j] combinations
    Dictionary<int, int> left = new Dictionary<int, int>();

    // loop over A[i] + B[j] combinations
    foreach (var a in A)
      foreach (var b in B) {
        int k = a + b;
        int v; 

        if (left.TryGetValue(k, out v))  
          left[k] = v + 1; // we have such a combination (v of them)
        else 
          left.Add(k, 1);  // we don't have such a combination
      }

    int result = 0;

    // loop over -C[k] - D[l] combinations
    foreach (var c in C)
      foreach (var d in D) {
        int v;

        if (left.TryGetValue(-c - d, out v)) 
          result += v;
      } 

    return result;
  }

如您所见,我们有O(|A| * |B| + |C| * |D|)复杂性;如果ABCD数组的大小大致相同N,则复杂度为O(N**2)

答案 1 :(得分:3)

你的第一步还可以。但是使用Dictionary代替List,这将确保持续时间查找并降低第二部分的复杂性。

这是我的C ++ O(n^2)解决方案:

int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
    int n = A.size();
    int result = 0;
    unordered_map<int,int> sumMap1;
    unordered_map<int,int> sumMap2;

    for(int i = 0; i < n; ++i) {
        for(int j = 0; j < n; ++j) {
            int sum1 = A[i] + B[j];
            int sum2 = C[i] + D[j];
            sumMap1[sum1]++;
            sumMap2[sum2]++;
        }
    }
    for(auto num1 : sumMap1) {
        int number = num1.first;
        if(sumMap2.find(-1 * number) != sumMap2.end()) {
            result += num1.second * sumMap2[-1 * number];
        }
    }
    return result;
}

核心观察是 - 如果W + X + Y + Z = 0然后W + X = -(Y + Z)

这里我在(A,B)和(C,D)两个可能的总和中使用了两个哈希表来查找此总和的出现次数。

然后,对于每个sum(A, B),我们可以找到sum(C, D)是否包含可确保sum(A, B) + sum(C, D) = 0的免费额。添加(sum(a, b))*的出现次数*(免费sum(c,d)的出现次数)到结果中。

创建sum(A, B)sum(C, D)将花费O(n^2)时间。计算元组的数量为O(n^2),因为每对(n^2A-B)总和为C-D。其他操作(如插入和搜索哈希表)是分摊O(1)。因此,整体时间复杂度为O(n^2)