简体   繁体   English

如何让这段代码找到一个更有效的和?

[英]How can I make this code to find a pair with a sum more efficient?

I'm doing this test on testdome.com for fun, and it's failing the efficiency test. 我正在testdome.com上做这个测试以获得乐趣,而且效率测试失败了。 What better way is there? 还有什么更好的方法? I'm not counting any values twice. 我没有计算任何值两次。 It seems the only way to do this is by brute force, which is an n^2 algorithm. 似乎唯一的方法是通过蛮力,这是一个n ^ 2算法。

Here are the directions for the problem: 以下是问题的方向:

Write a function that, given a list and a target sum, returns zero-based indices of any two distinct elements whose sum is equal to the target sum. 编写一个函数,给定一个列表和一个目标和,返回任何两个不同元素的从零开始的索引,这两个元素的总和等于目标总和。 If there are no such elements, the function should return null. 如果没有这样的元素,则该函数应返回null。

For example, findTwoSum(new int[] { 1, 3, 5, 7, 9 }, 12) should return any of the following tuples of indices: 1, 4 (3 + 9 = 12), 2, 3 (5 + 7 = 12), 3, 2 (7 + 5 = 12) or 4, 1 (9 + 3 = 12). 例如,findTwoSum(new int [] {1,3,5,7,9},12)应该返回以下任何索引元组:1,4(3 + 9 = 12),2,3(5 + 7 = 12),3,2(7 + 5 = 12)或4,1(9 + 3 = 12)。

And here's my code: 这是我的代码:

public class TwoSum {
public static int[] findTwoSum(int[] list, int sum) {
    if (list == null || list.length < 2) return null;
    for (int i = 0; i < list.length - 1; i++) { //lower indexed element
        for (int j = i + 1; j < list.length; j++) { //higher indexed element
            if (list[i] + list[j] == sum) {
                return new int[]{i, j};
            }
        }
    }

    //none found  
    return null;
}


public static void main(String[] args) {
    int[] indices = findTwoSum(new int[] { 1, 3, 5, 7, 9 }, 12);
    System.out.println(indices[0] + " " + indices[1]);
}

} }

EDIT: So here's my final working code. 编辑:所以这是我最后的工作代码。 Thanks everyone! 感谢大家!

import java.util.HashMap;
import java.util.Map;

public class TwoSum {
    public static int[] findTwoSum(int[] list, int sum) {
        if (list == null || list.length < 2) return null;
        //map values to indexes
        Map<Integer, Integer> indexMap = new HashMap<>();
        for (int i = 0; i < list.length; i++) {
            indexMap.put(list[i], i);
        }

        for (int i = 0; i < list.length; i++) {
            int needed = sum - list[i];
            if (indexMap.get(needed) != null) {
                return new int[]{i, indexMap.get(needed)};
            }
        }

        //none found
        return null;
    }

    public static void main(String[] args) {
        int[] indices = findTwoSum(new int[] { 1, 3, 5, 7, 9 }, 12);
        System.out.println(indices[0] + " " + indices[1]);
    }
}

As per Kon's suggestion, in one pass: 按照Kon的建议,一次通过:

public static int[] findTwoSum(int[] list, int sum) {
    if (list == null || list.length < 2) return null;
    //map values to indexes
    Map<Integer, Integer> indexMap = new HashMap<>();
    for (int i = 0; i < list.length; i++) {
        int needed = sum - list[i];
        if (indexMap.get(needed) != null) {
            return new int[]{i, indexMap.get(needed)};
        }

        indexMap.put(list[i], i);
    }

    //none found
    return null;
}

Look at what you do in the inner loop, your checking if list[i] + list[j] == sum. 看看你在内循环中做了什么,检查列表[i] + list [j] == sum。

If you transform the equation slightly, it means given list[i] and sum (which are both constants within the inner loop), you are really asking "is there an index where the value (sum - list[i]) is stored", and thats what your inner loop solves. 如果你稍微改变方程,它意味着给定list [i]和sum(它们都是内循环中的常量),你真的在​​问“是否存在值(sum-list [i])存储的索引” ,那就是你的内循环解决的问题。

Now applying the knowledge that you could solve the problem using essentially a indexOf(sum - list[i])-style method in linear time, there are data structures that can answer this kind of question in better time than O(N). 现在应用基本上使用indexOf(sum - list [i]) - 样式方法在线性时间内解决问题的知识,有一些数据结构可以在比O(N)更好的时间内回答这类问题。

Here is linear solution (save sorting which is O(n*log(n)) ): 这是线性解决方案 (保存排序为O(n * log(n))):
1) Sort your initial array a[] 1)对初始数组a []进行排序
2) let i be the first index of a[], and j - the last 2)让我成为[]的第一个索引,j - 最后一个

i = 0;
j = a[].length - 1;

3) lets move from two ends: 3)让我们从两端移动:

do{
  if(a[i]+a[j] < sum)
    i++;
  else if(a[i]+a[j] > sum)
    j--;
  else { // we have found required indexes!
     put (i, j) to result set;
     i++;
  }
} while(i < j);

The final result - set of pairs (i,j) that give required sum. 最终结果 - 给出所需总和的对(i,j)的集合。
You can stop after first pair and return it. 您可以在第一对之后停下来并将其退回。

PS if you have array like {3, 3, 3, 3, 9, 9, 9, 9} this solution will not give all the combinations:) PS,如果你有像{3,3,3,3,9,9,9,9}这样的数组,这个解决方案将不会给出所有的组合:)

public static Map<Integer, Integer> findTwoSum(int[] list, int sum) {
    if (list == null || list.length < 2) return null;
    Map<Integer, Integer> indexMap = new HashMap<Integer, Integer>();
    Map<Integer, Integer> arrayResult = new HashMap<Integer, Integer>();
    for (int i = 0; i < list.length; i++) {
        indexMap.put(list[i], i);
    }

    for (int i = 0; i < list.length; i++) {
        int needed = sum - list[i];
        if (indexMap.get(needed) != null) {
            arrayResult.put(i, indexMap.get(needed));
        }
    }
    return arrayResult.isEmpty()?null:arrayResult;
}

public static void main(String[] args) {
    Map<Integer, Integer> indices = findTwoSum(new int[] { 1, 3, 5, 7, 9 }, 12);
    System.out.println(indices);
}

Here's a solution written in C#. 这是用C#编写的解决方案。 It should be easy enough to convert it over to Java lingo: 将它转换为Java术语应该很容易:

static public IEnumerable<Tuple<int, int>> FindAllTwoSumIndexes(IList<int> list, long desiredSum)
{
    var count = list?.Count;
    if (list == null || count <= 1)
        return null;

    var results = new List<Tuple<int, int>>(32);
    var indexesMap = new ConcurrentDictionary<long, List<int>>(); //0 value-to-indexes
    for (var i = 0; i < count; i++)
    {
        var thisValue = list[i];
        var needed = desiredSum - thisValue;
        if (indexesMap.TryGetValue(needed, out var indexes))
        {
            results.AddRange(indexes.Select(x => Tuple.Create(x, i)));
        }

        indexesMap.AddOrUpdate(
            key: thisValue,
            addValueFactory: x => new List<int> { i },
            updateValueFactory: (x, y) =>
            {
                y.Add(i);
                return y;
            }
        );
    }

    return results.Any() ? results.OrderBy(x => x.Item1).ThenBy(x => x.Item2).ToList() : null;

    //0 bare in mind that the same value might be found over multiple indexes   we need to take this into account
    //  also note that we use concurrentdictionary not for the sake of concurrency or anything but because we like
    //  the syntax of the addorupdate method which doesnt exist in the simple dictionary
}

Here is a Java program which find the pair of values in the array whose sum is equal to k using Hashtable or Set in the most efficient way. 这是一个Java程序,它使用Hashtable或Set以最有效的方式查找数组中的值对,其总和等于k。

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class ArraySumUsingSet {

public static void main(String args[]) {
   prettyPrint(getRandomArray(9), 11);
   prettyPrint(getRandomArray(10), 12);
}

/**
 * Given an array of integers finds two elements in the array whose sum is equal to n.
 * @param numbers
 * @param n
 */
public static void printPairsUsingSet(int[] numbers, int n){
    if(numbers.length < 2){
        return;
    }        
    Set set = new HashSet(numbers.length);

    for(int value : numbers){
        int target = n - value;

        // if target number is not in set then add
        if(!set.contains(target)){
            set.add(value);
        }else {
            System.out.printf("(%d, %d) %n", value, target);
        }
    }
}

/*
 * Utility method to find two elements in an array that sum to k.
 */
public static void prettyPrint(int[] random, int k){
    System.out.println("Random Integer array : " + Arrays.toString(random));
    System.out.println("Sum : " + k);
    System.out.println("pair of numbers from an array whose sum equals " + k);
    printPairsUsingSet(random, k);
}

/**
 * Utility method to return random array of Integers in a range of 0 to 15
 */
public static int[] getRandomArray(int length){
    int[] randoms = new int[length];
    for(int i=0; i<length; i++){
        randoms[i] = (int) (Math.random()*15);
    }
    return randoms;
}

}

Output 产量

Random Integer array : [0, 14, 0, 4, 7, 8, 3, 5, 7]
Sum : 11
pair of numbers from an array whose sum equals 11
(7, 4)
(3, 8)
(7, 4)
Random Integer array : [10, 9, 5, 9, 0, 10, 2, 10, 1, 9]
Sum : 12
pair of numbers from an array whose sum equals 12
(2, 10)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM