Java:如何实现3和?

时间:2016-12-01 08:20:55

标签: java arrays algorithm list

我正在研究3 Sum以自己实现它,并且遇到了以下实施规则:

  

给定n个整数的数组S,S中是否有元素a,b,c,a + b + c = 0?找到数组中所有唯一的三元组,它们总和为零。

     

注意:三元组(a,b,c)中的元素必须按非降序排列。 (即a≤b≤c)   解决方案集不得包含重复的三元组。

For example, given array S = {-1 0 1 2 -1 -4},

A solution set is:
(-1, 0, 1)
(-1, -1, 2)

实现(对数组进行排序,遍历列表,并使用另外两个指针来接近目标):

import java.util.*;

public class ThreeSum {
    List<List<Integer>> threeSum(int[] num) {
        Arrays.sort(num);
        List<List<Integer>> res = new LinkedList<>(); 

        for (int i=0; i<num.length-2; i++) {
            if (i==0 || (i>0 && num[i] != num[i-1])) { //HERE
                int lo = i+1;
                int hi = num.length-1;
                int sum = 0 - num[i];

                while (lo < hi) {
                    if (num[lo] + num[hi] == sum) {
                        res.add(Arrays.asList(num[i], num[lo], num[hi]));
                        while (lo < hi && num[lo] == num[lo+1]) lo++; //HERE
                        while (lo < hi && num[hi] == num[hi-1]) hi--; //HERE
                        lo++; hi--;

                    } else if (num[lo] + num[hi] < sum) lo++;
                    else hi--; 
               }
            }
        }

        return res;
    }

    //Driver
    public static void main(String args[]) {
        ThreeSum ts = new ThreeSum();
        int[] sum = {-1, 0, 1, 2, -1, -4};

        System.out.println(ts.threeSum(sum));
    }
}

我的问题是(位于评论://这里),检查num[i] != num[i-1]num[lo] == num[lo+1]num[hi] == num[hi-1]的原因是什么?据说他们应该跳过相同的结果,但这意味着什么?例子真的会有所帮助。

提前感谢您,并接受回答/投票。

5 个答案:

答案 0 :(得分:1)

想象一下你有{-1,-1,0,1,2,4}并考虑三元组[0],num [2],num [3]( - 1,0,1)。

lo=0在这里。要排除具有相同值的三元组num [1],num [2],num [3],我们应该增加lo并传递重复

答案 1 :(得分:1)

这将阻止列表具有重复的三元组。

例如,随你测试:

int[] sum = {-1, 0, 1, 2, -1, -4};

的排序方式如下:

sum = {-4, -1, -1, 0, 1, 2};

您看到自己有-1两次。如果没有这些测试,如果-1 = 0 + 1,您将测试两次。这没用,所以算法只搜索下一个不同的值。

您可以删除已排序列表中的副本以阻止这些测试。

感谢MBo,我们无法删除重复项,因为我们可以使用相同值的三元组(但索引不同)

答案 2 :(得分:1)

所有三个句子用于避免重复输出。
考虑排序列表

docker swarm join

如果没有检查{-2, -2 , 1, 1},则该计划的输出将是num[i] != num[i-1](-2, 1, 1)

这两个重复的三元组。<登记/>
检查(-2, 1, 1)num[lo] != num[lo + 1]的原因相同。 考虑一个排序列表

num[hi] != num[hi - 1]

如果没有检查num [lo],您将获得{-2,-1,-1,0,3} (-2,-1,3)作为输出。

不过,我想为这个问题推荐一个更好的解决方案。您可以计算列表中两个数字的总和,并通过散列或二进制搜索找到第三个数字。它将帮助您获得O(n ^ 2logn)时间复杂度而不是O(n ^ 3)。 (我错了,这个算法的时间复杂度是O(n ^ 2),对不起。)

答案 3 :(得分:1)

以下程序找到具有O(N * 2)

的三个整数对
  1. 对输入数组进行排序
  2. 并迭代for循环中的每个元素,并检查为两个和开发的程序中的和。
  3. 排序后的线性时间的两个总和 - &gt; https://stackoverflow.com/a/49650614/4723446

    公共课ThreeSum {

    button {
      display: inline-block;
      margin: 0 auto;
    }
    

    }

答案 4 :(得分:1)

它适用于任何 NSum(3Sum、4Sum、5Sum 等)并且速度非常快。

public class ThreeSum {
private static final int RANDOM_RANGE = 20;
private Integer array[];
private Integer arrayIndex[];
private int result[];
private int bagLength;
private int resultIndex = 0;

private void generateData(int size) {
    array = new Integer[size];
    Random random = new Random();
    for (int i = 0; i < size; i++) {
        array[i] = random.nextInt(RANDOM_RANGE) - (RANDOM_RANGE/2);
    }
}

private void markArrayIndex(int size) {
    arrayIndex = new Integer[size];
    for (int i = 0; i < size; i++) {
        arrayIndex[i] = i;
    }
}

private void prepareBeforeCalculate(int size, int sumExpected, int bagLength) {
    this.bagLength = bagLength;
    result = new int[bagLength];
    generateData(size);
    markArrayIndex(size);
}

void calculate(int size, int sumExpected, int bagLength) {
    prepareBeforeCalculate(size, sumExpected, bagLength);

    Arrays.sort(arrayIndex, (l, r) -> array[l].compareTo(array[r]));
    System.out.println(Arrays.toString(array));

    long startAt = System.currentTimeMillis();
    if (sumExpected > 0) findLeft(sumExpected, 0, 0, array.length);
    else findRight(sumExpected, 0, 0 - 1, array.length - 1);

    System.out.println("Calculating in " + ((System.currentTimeMillis() - startAt) / 1000));
}

private void findLeft(int total, int indexBag, int left, int right) {
    while (left < array.length && array[arrayIndex[left]] < 0 && indexBag < bagLength) {
        navigating(total, arrayIndex[left], indexBag, left, right);
        left++;
    }
}

private void findRight(int total, int indexBag, int left, int right) {
    while (right >= 0 && array[arrayIndex[right]] >= 0 && indexBag < bagLength) {
        navigating(total, arrayIndex[right], indexBag, left, right);
        right--;
    }
}

private void navigating(int total, int index, int indexBag, int left, int right) {
    result[indexBag] = index;
    total += array[index];
    if (total == 0 && indexBag == bagLength - 1) {
        System.out.println(String.format("R[%d] %s", resultIndex++, toResultString()));
        return;
    }

    if (total > 0) findLeft(total, indexBag + 1, left + 1, right);
    else findRight(total, indexBag + 1, left, right - 1);
}

private String toResultString() {
    int [] copyResult = Arrays.copyOf(result, result.length);
    Arrays.sort(copyResult);

    int iMax = copyResult.length - 1;
    StringBuilder b = new StringBuilder();
    b.append('[');
    for (int i = 0; ; i++) {
        b.append(array[copyResult[i]]);
        if (i == iMax)
            return b.append(']').toString();
        b.append(", ");
    }
}

}

public class ThreeSumTest {
@Test
public void test() {
    ThreeSum test = new ThreeSum();
    test.calculate(100, 0, 3);
    Assert.assertTrue(true);
}

}