返回a(a,b)的总数,其中a来自A,b来自B,a + b是< = c

时间:2016-11-15 06:07:24

标签: java algorithm

尝试做一些练习,我遇到了这个问题......

  

给定两个int数组A和B以及一个int c,返回对的总数       (a,b)其中a来自A,b来自B,a + b是< = c

立即提出蛮力解决方案,但似乎无法连接这些点,以便在更好的时间复杂度下实现这一点。我首先尝试对数组进行排序,并试图找到某种类型的模式,但这并没有让我到任何地方。我想过一个数组有负数的情况。在这种情况下,我不能只查看A或B中的值并检查它本身是否小于c,因为在另一个数组中可能有一个负值,当它们加在一起时会得到< = c的结果。任何见解,想法或线索将不胜感激。

import java.util.*;

public class CountPairs {

    public static void main(String args[]){
        int arrayA[] = {32,45,9,4};
        int arrayB[] = {43,457,54,90};

        Scanner scan = new Scanner(System.in);

        System.out.println("Choose the value that you want to find pairs <= ");
        int c = scan.nextInt();

        System.out.println("The total number of pairs are : " + pairs(arrayA, arrayB, c));
    }

    public static int pairs(int A[], int B[], int n) {
        int count = 0;

        for (int i = 0; i < A.length; i++) {
            for (int j = 0; j < B.length; j++) {
                int total = A[i] + B[j];

                if(total <= n)
                    count++;
            }
        }

        return count;
    }
}

6 个答案:

答案 0 :(得分:11)

让我们花一点时间来了解使用Javaslang并使用声明性功能方法时任务变得多么容易:

初始数据:

int arrayA[] = {32, 45, 9, 4};
int arrayB[] = {43, 457, 54, 90};

int c = 100;

<强>解决方案:

int result = List.ofAll(arrayA)
  .crossProduct(List.ofAll(arrayB))
  .distinct()
  .count(t -> t._1 + t._2 <= c);

答案 1 :(得分:6)

如果您是为了练习而这样做,那么我建议您完全忽略性能,并希望明确代码。

开发人员通常习惯于以简单和清晰为代价使代码尽可能高效,这通常是一个坏主意,因为几乎不可能提前告诉什么是性能问题。

就清晰度而言,您可能需要考虑使用Stream API而不是常见的迭代:

Arrays.stream(arrayA)
    .flatMap(n1 -> Arrays.stream(arrayB).map(n2 -> n1 + n2))
    .filter(n -> n <= total)
    .count();

答案 2 :(得分:6)

我们可以在O(m log m + n log m) = O(log m (m + n))时间内解决此问题,其中m是较小数组的基数; n,更大。首先,对较小的数组进行排序:

A = {32,45,9,4};
B = {43,457,54,90};

=> A = {4,9,32,45}

现在b中的每个B使用A上的二进制搜索来查找最大a小于或等于(c - b)的索引。将(index + 1)添加到累积结果中。例如:

c = 100:
  43  => 100 - 43  = 57   => largest a <= c-b = 45, index 3 => add 4 to result
  457 => 100 - 457 = -357 => largest a <= c-b = None
  54  => 100 - 54  = 46   => largest a <= c-b = 45, index 3 => add 4 to result
  90  => 100 - 90  = 10   => largest a <= c-b = 9,  index 1 => add 2 to result

result = 4 + 4 + 2 = 10
 corresponding with the following pairs:
 (43,4),(43,9),(43,32),(43,45),(54,4),(54,9),(54,32),(54,45),(90,4),(9,9)

答案 3 :(得分:3)

从排序数组开始是个好主意。我从最大值向下工作,找到哪些索引在c下给出一个值:

public static int pairs(int a[], int b[], int c) {
    // a and b should be sorted before being passed here

    // Find the largest a that has a pair under c:
    int amax;
    int bmax_for_a;
    for (amax = a.length; amax > 0; amax--) {
        for (int bmax_for_a = b.length; bmax_for_a > 0; bmax_for_a--) {
            if (a[amax] + b[bmax_for_a] <= c) {
                break;
            }
        }
    }

    // Find the largest b that has a pair under c:
    int bmax;
    int amax_for_b;
    for (bmax = b.length; bmax > 0; bmax--) {
        for (int amax_for_b = a.length; amax_for_b > 0; amax_for_b--) {
            if (a[amax_for_b] + b[bmax] <= c) {
                break;
            }
        }
    }

    // Now that we have the indexes, calculate the total matches
    // and discard duplicates:
    return (amax * bmax_for_a) + (bmax * amax_for_b) - (amax_for_b * bmax_for_a);
}

答案 4 :(得分:2)

我喜欢对两个列表进行排序并搜索边缘情况的想法。然而,只有当数组中的数字具有良好的模式时,这才能正常工作。 (例如{1, 2, 3' ...}{1, 3, 5, ...})因为您可以为该特定模式设计特定的算法。

减少不必要的循环的方法是对两个数组进行排序,并得到最小的数字。并检查你的索引,你不必再寻找对了。喜欢
- 想象A= {1, 5, 6, 12}B {1, 2, 3, 4, 5, 6}c是7 - 对于阵列A,最小的数字是1。这意味着在数组B中的索引5之外没有匹配,这意味着你必须迭代到索引5 - 对于数组B,最低数字也是1。超越指数2,不应该有任何可能的对。

这样你可以切断两个数组的一部分,但是你仍然需要在那个点之前检查每个索引,因为没有保证所有可能的对都满足预设条件。

答案 5 :(得分:2)

如何利用NavigableSet进行排序及其headSet方法:

public int countPairs(int[] a, int[] b, int c) {
    NavigableSet<Integer> aSet = IntStream.of(a).boxed().collect(Collectors.toCollection(TreeSet::new));
    NavigableSet<Integer> bSet = IntStream.of(b).boxed().collect(Collectors.toCollection(TreeSet::new));

    int total = 0;

    for (int aVal : aSet) {
        int max = c - aVal;
        Set<Integer> bVals = bSet.headSet(max, true);
        total += bVals.size();

        // Optimization to break from loop once there are no values small enough to satisfy the condition
        if (bVals.isEmpty()) {
            break;
        }
    }

    return total;
}

这确实假设a = [0, 1], b = [0, 1], c = 50, 11, 0是不同的对。它还将每个数组中的重复项集中在一起,但这可以通过一些添加的记录保存来处理(这使得算法更难以遵循)。