Java 确定集合S中是否存在两个元素,它们的和恰好是x-正确解?

Java 确定集合S中是否存在两个元素,它们的和恰好是x-正确解?,java,arrays,algorithm,Java,Arrays,Algorithm,摘自算法简介 描述一个Θ(n lgn)-时间算法 给定一组n个整数和 另一个整数x,确定 或者说S中是否存在两种元素 其和正好是x 这是迄今为止我用Java实现的最佳解决方案: public static boolean test(int[] a, int val) { mergeSort(a); for (int i = 0; i < a.length - 1; ++i) { int diff = (val >= a[i]) ? val -

摘自算法简介

描述一个Θ(n lgn)-时间算法 给定一组n个整数和 另一个整数x,确定 或者说S中是否存在两种元素 其和正好是x

这是迄今为止我用Java实现的最佳解决方案:

    public static boolean test(int[] a, int val) {
    mergeSort(a);

    for (int i = 0; i < a.length - 1; ++i) {
        int diff = (val >= a[i]) ? val - a[i] : a[i] - val;

        if (Arrays.binarySearch(a, i, a.length, diff) >= 0) {
            return true;
        }
    }

    return false;
}
公共静态布尔测试(int[]a,int val){
合并(a);
对于(int i=0;i=a[i])?val-a[i]:a[i]-val;
if(Arrays.binarySearch(a,i,a.length,diff)>=0){
返回true;
}
}
返回false;
}
现在我的第一个问题是:这是正确的解决方案吗?根据我的理解,mergeSort应该在O(n lgn)中执行排序,循环应该取O(n lgn)(n表示迭代乘以O(lgn)表示二进制搜索,结果是O(2n lgn),因此它应该是正确的


我的第二个问题是:有没有更好的解决方案?数组排序是否必要?

您的分析是正确的,是的,您必须对数组进行排序,否则二进制搜索无法工作。

您的解决方案似乎很好。是的,您需要排序,因为这是二进制搜索的先决条件。您可以对逻辑稍作修改,如下所示:

public static boolean test(int[] a, int val) 
{
    Arrays.sort(a);

    int i = 0;            // index of first element.
    int j = a.length - 1; // index of last element. 

    while(i<j)
    {
        // check if the sum of elements at index i and j equals val, if yes we are done.
        if(a[i]+a[j] == val)
            return true;
        // else if sum if more than val, decrease the sum.
        else if(a[i]+a[j] > val)
            j--;
        // else if sum is less than val, increase the sum.
        else
            i++;
    }
    // failed to find any such pair..return false. 
    return false;
}
公共静态布尔测试(int[]a,int val)
{
数组。排序(a);
int i=0;//第一个元素的索引。
int j=a.length-1;//最后一个元素的索引。
while(i val)
j--;
//否则,如果总和小于val,则增加总和。
其他的
i++;
}
//找不到任何这样的对..返回false。
返回false;
}

我确实认为我在您的实现中发现了一个小错误,但是测试应该会很快发现这个错误

该方法看起来有效,并将达到所需的性能。您可以通过将迭代二进制搜索替换为对数组的扫描来简化该方法,实际上,将二进制搜索替换为线性搜索,并在上一次线性搜索停止时恢复:

int j = a.length - 1;
for (int i = 0; i < a.length; i++) {
    while (a[i] + a[j] > val) {
        j--;
    }
    if (a[i] + a[j] == val) {
        // heureka!
    }
}
intj=a.length-1;
for(int i=0;ival){
j--;
}
如果(a[i]+a[j]==val){
//海瑞卡!
}
}
这一步是O(n)。(证明这一点留给您作为练习。)当然,整个算法仍然需要O(n logn)作为合并排序

  • 这是正确的;您的算法将在O(n lgn)时间内运行

  • 有一个更好的解决方案:计算diff的逻辑不正确。无论
    a[i]
    是大于还是小于
    val
    ,您仍然需要diff是
    val-a[i]

  • 下面是一个使用哈希集的O(n)解决方案:

      public static boolean test(int[] a, int val) {
          Set<Integer> set = new HashSet<Integer>();
    
          // Look for val/2 in the array
          int c = 0;
          for(int n : a) {
            if(n*2 == val)
              ++c
          }
          if(c >= 2)
             return true; // Yes! - Found more than one
    
          // Now look pairs not including val/2
          set.addAll(Arrays.asList(a));
          for (int n : a) {
             if(n*2 == val)
                continue;
             if(set.contains(val - n))
                return true;
          }
    
          return false;
       }
    
    公共静态布尔测试(int[]a,int val){
    Set=newhashset();
    //在数组中查找val/2
    int c=0;
    对于(int n:a){
    如果(n*2==val)
    ++c
    }
    如果(c>=2)
    返回true;//是!-找到多个
    //现在查看不包括val/2的配对
    set.addAll(Arrays.asList(a));
    对于(int n:a){
    如果(n*2==val)
    继续;
    if(set.contains(val-n))
    返回true;
    }
    返回false;
    }
    
    还有另一个非常快速的解决方案:假设您必须用Java解决这个问题,大约需要10亿个整数。您知道,在Java中,整数从
    -2**31+1
    +2**31

    创建一个具有
    2**32
    10亿位(500 MB,在今天的硬件上做起来很简单)的阵列

    在集合上迭代:如果您有一个整数,则将相应的位设置为1

    到目前为止

    在集合上再次迭代:对于每个值,检查是否在“current val-x”处设置了位

    如果有,则返回true

    当然,它需要500 MB的内存

    但是,如果你有,比如说,用10亿个整数来解决这个问题,这个问题将围绕任何其他的O(n logn)解运行


    O(n).

    一个简单的解决方案是,排序后,将指针从数组的两端向下移动,寻找和x的对。如果和太高,则减少右指针。如果太低,则增加左指针。如果指针交叉,答案是否。

    这里是一个替代解决方案,通过在mergesort中添加更多的条件

    public static void divide(int array[], int start, int end, int sum) {
    
        if (array.length < 2 || (start >= end)) {
            return;
        }
        int mid = (start + end) >> 1; //[p+r/2]
        //divide
        if (start < end) {
            divide(array, start, mid, sum);
            divide(array, mid + 1, end, sum);
            checkSum(array, start, mid, end, sum);
        }
    }
    
    private static void checkSum(int[] array, int str, int mid, int end, int sum) {
    
        int lsize = mid - str + 1;
        int rsize = end - mid;
        int[] l = new int[lsize]; //init
        int[] r = new int[rsize]; //init
    
        //copy L
        for (int i = str; i <= mid; ++i) {
            l[i-str] = array[i];
        }
        //copy R
        for (int j = mid + 1; j <= end; ++j) {
            r[j - mid - 1] = array[j];
        }
        //SORT MERGE
        int i = 0, j = 0, k=str;
        while ((i < l.length) && (j < r.length) && (k <= end)) {
        //sum-x-in-Set modification
        if(sum == l[i] + r[j]){
            System.out.println("THE SUM CAN BE OBTAINED with the values" + l[i] + " " + r[j]);            
        }
         if (l[i] < r[j]) {
                array[k++] = l[i++];
            } else {
                array[k++] = r[j++];
            }
        }
        //left over
        while (i < l.length && k <= end) {
            array[k++] = l[i++];
              //sum-x-in-Set modification
            for(int x=i+1; x < l.length; ++x){
                if(sum == l[i] + l[x]){
                    System.out.println("THE SUM CAN BE OBTAINED with the values" + l[i] + " " + l[x]);
                }
            }
        }
        while (j < r.length && k <= end) {
            array[k++] = r[j++];
              //sum-x-in-Set modification
            for(int x=j+1; x < r.length; ++x){
                if(sum == r[j] + r[x]){
                    System.out.println("THE SUM CAN BE OBTAINED with the values" + r[j] + " " + r[x]);
                }
            }
        }
    }
    
    publicstaticvoiddivide(int数组[],int开始,int结束,int和){
    if(array.length<2 | |(start>=end)){
    返回;
    }
    int mid=(开始+结束)>>1;//[p+r/2]
    //分开
    如果(开始<结束){
    除法(数组、开始、中间、和);
    除法(数组,中间+1,结束,和);
    校验和(数组、开始、中间、结束、和);
    }
    }
    私有静态无效校验和(int[]数组、int str、int mid、int end、int sum){
    int lsize=mid-str+1;
    int rsize=结束-中间;
    int[]l=newint[lsize];//初始化
    int[]r=newint[rsize];//初始化
    //副本L
    
    对于(int i=str;i is heureka是启发式和eureka的混合体?@Bishiboosh:不,是希腊单词的德语音译。我不知道英文音译去掉了H。你从堆栈溢出中学到的东西…:-)你可以使用哈希集吗?如果可以,那么你可以使用O(n),见下文。
    (val>=a[i])val-a[i]:a[i]-val
    这就是
    Math.abs()
    的作用:)是的,
    Math.abs
    就是为了这个,但我想说的更多:这里不需要它。事实上是错误的。
    diff
    必须始终被赋值
    val-a[I]
    无论这种差异是积极的还是消极的。正如下面几个答案中所建议的,有一个更好的O(n)sol'n给出了一些关于输入的假设。是的,排序似乎是必要的,但一旦你有了一个已排序的列表,你就可以改进。不要天真地使用二进制搜索,你可以考虑以下方法:初始化指针l,r到列表的开始和结束。选中a[l]+a[r]=f;如果