繁体   English   中英

确定Set S中是否存在两个元素,其总和正好是x - 正确的解?

[英]Determine whether or not there exist two elements in Set S whose sum is exactly x - correct solution?

摘自算法简介

描述一个Θ(n lg n)时间算法,给定一组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;
}

现在我的第一个问题是:这是一个正确的解决方案吗? 根据我的理解,mergeSort应该在O(n lg n)中执行排序,循环应该取O(n lg n)(n用于迭代乘以O(lg n)进行二进制搜索,得到O(2 l lg) n),所以它应该是正确的。

我的第二个问题是:有更好的解决方案吗? 排序阵列必不可少?

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

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

还有另一个非常快速的解决方案:想象一下,你必须在Java中解决这个大约10亿个整数的问题。 你知道在Java中整数从-2**31+1+2**31

创建一个2**32亿位的数组(500 MB,在今天的硬件上做的很简单)。

迭代你的集合:如果你有一个整数,将相应的位设置为1。

到目前为止O(n)。

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

如果你有一个,你就会返回true。

当然,它需要500 MB的内存。

但是,如果您有10亿个整数来解决这个问题,那么这应该围绕任何其他O(n log n)解决方案运行。

上)。

  1. 这是对的; 你的算法将在O(n lg n)时间内运行。

  2. 有一个更好的解决方案:您计算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;
   }

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

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

该方法看起来有效,并将达到所需的性能。 您可以通过使用扫描数组替换迭代二进制搜索来简化它,实际上通过线性搜索替换二进制搜索,线性搜索将恢复前一个线性搜索中断的位置:

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!
    }
}

该步骤是O(n)。 (证明这是留给你的练习。)当然,整个算法仍然需要O(n log n)进行合并排序。

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

这是一个替代解决方案,通过向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]);
            }
        }
    }
}

但这种算法的复杂性仍不等于THETA(nlogn)

暂无
暂无

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

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