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

时间:2014-11-21 17:26:11

标签: java performance algorithm

我在testdome.com上进行this测试是为了好玩,而且它没有通过效率测试。还有什么更好的方法?我没有计算任何两次的值。似乎唯一的方法是使用强力,这是一种n ^ 2算法。

以下是问题的说明:

  

编写一个函数,给定一个列表和一个目标总和,返回   任何两个不同元素的从零开始的索引,其总和等于   目标总和。如果没有这样的元素,函数应该   return null。

     

例如,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)。

这是我的代码:

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]);
}

}

编辑:所以这是我的最终工作代码。谢谢大家!

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]);
    }
}

根据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;
}

5 个答案:

答案 0 :(得分:2)

看看你在内循环中做了什么,检查列表[i] + list [j] == sum。

如果稍微改变等式,则意味着给定list [i]和sum(它们都是内循环中的常量),你真的在​​问“是否有一个索引所在的值(sum - list [i])存储“,这就是你的内循环解决的问题。

现在应用基本上使用indexOf(sum - list [i]) - 样式方法在线性时间内解决问题的知识,有一些数据结构可以在比O(N更好的时间内回答这类问题)。

答案 1 :(得分:1)

这是线性解决方案(保存排序为O(n * log(n))):
1)对初始数组a []进行排序 2)让我成为[]的第一个索引,j - 最后一个

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

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);

最终结果 - 给出所需总和的对(i,j)的集合。
你可以在第一对后停下来然后将它归还。

P.S。如果你有像{3,3,3,3,9,9,9,9这样的数组},这个解决方案将不会给出所有组合:)

答案 2 :(得分:0)

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);
}

答案 3 :(得分:0)

这是用C#编写的解决方案。将其转换为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
}

答案 4 :(得分:-1)

这是一个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;
}

}

输出

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)