在2个排序数组(每个数组中有1个值)中找到值对,其总和最接近目标值

时间:2018-08-04 17:30:05

标签: java arrays optimization dynamic-programming closest

原始问题有2个整数的未排序列表。为了简化此问题,我们只考虑输入是2个排序的整数数组和一个整数目标。如果解决方案对超过1个,则值对可以重复。

例如:[7,8,14],[5,10,14]目标:20 解决方案是:[14,5]为第一个数组的14,第二个数组的和19的5为5,最接近20。

我的解决方案是从头到尾遍历两个数组,并与跟踪的最小差异进行比较,并在新差异较小时进行更新。

但这是蛮力。有更好的解决方案吗?

我在网上找到的大多数解决方案都是从同一阵列中找到目标,在2个阵列目标问题和1个阵列之间是否有相似之处?

2 个答案:

答案 0 :(得分:2)

一个关键的见解:给定一个对(x,y),其总和高于目标,则该总和比任何对(x,y')的总和更近,其中y'> y。相反,如果(x,y)的总和低于目标,则该总和比x'

这将产生一个线性时间算法:

  1. 从列表X的第一个元素开始,然后从列表Y的最后一个元素开始
  2. 检查到目前为止是否是最好的一对(如果这样,请记住它)
  3. 如果该总和小于目标,请移至X的下一个较高元素。如果该总和大于目标,请移至Y的下一个较低元素
  4. 循环步骤2-3,直到X或Y中的元素用完

在Java中:

private static Pair<Integer, Integer> findClosestSum(List<Integer> X, List<Integer> Y, int target) {
    double bestDifference = Integer.MAX_VALUE;
    Pair<Integer, Integer> bestPair = null;
    int xIndex = 0;
    int yIndex = Y.size() - 1;

    while (true) {
        double sum = X.get(xIndex) + Y.get(yIndex);
        double difference = Math.abs(sum - target);
        if (difference < bestDifference) {
            bestPair = new Pair<>(X.get(xIndex), Y.get(yIndex));
            bestDifference = difference;
        }

        if (sum > target) {
            yIndex -= 1;
            if (yIndex < 0) {
                return bestPair;
            }
        } else if (sum < target) {
            xIndex += 1;
            if (xIndex == X.size()) {
                return bestPair;
            }
        } else {
            // Perfect match :)
            return bestPair;
        }
    }
}

通过开始段落中的逻辑,您可以证明此算法是详尽无遗的。对于访问过的任何一对,都必须有一对,其中包含 被访问过的两个元素之一,并且其总和严格接近目标。< / p>

编辑:如果您只想要小于目标的总和(而不是超出目标的总和),则仍然适用相同的逻辑。在超调情况下,(x,y')与(x,y)一样无效,因此它不能成为更好的候选总和。在这种情况下,只需修改第2步,以仅在到目前为止是最接近的不超过总和的情况下存储总和。

答案 1 :(得分:0)

谢谢您的算法,我已经实现了我的逻辑。是的,它确实需要是最接近目标的一对,因此我对代码进行了相应的更改。由于输入可能是重复的,因此我也确保了句柄。结果也可能是多个,因此也可以处理。如果您发现任何潜在的优化方法,请告诉我。这是代码:

  public static List<List<Integer>> findClosest(int[] x, int[] y, int target){
         List<List<Integer>> result = new ArrayList<List<Integer>>();
         int[] pair = new int[2];
         int bestDiff = Integer.MIN_VALUE;
         int xIndex = 0;
         int yIndex = y.length - 1;
         //while left doesn't reach left end and right doesn't reach right end
         while(xIndex < x.length && yIndex >= 0){
             int xValue = x[xIndex];
             int yValue = y[yIndex];
             int diff = xValue + yValue - target;
             //values greater than target, y pointer go right
             if(diff > 0){
                 yIndex--;
                 while(yIndex > 0 && yValue == y[yIndex - 1]) yIndex--;
             }else{//combined == 0 which match target and < 0 which means the sum is less than target
                 //duplicates result, just add
                 if(diff == bestDiff){
                     result.add(Arrays.asList(xValue, yValue));
                 }
                 //found better pair, clear array and add new pair
                 else if(diff > bestDiff){
                     result.clear();
                     result.add(Arrays.asList(xValue, yValue));
                     bestDiff = diff;
                 }
                 xIndex++;
             }
         }
         return result;
  }