Подтвердить что ты не робот

Определите, существуют ли в наборе S два элемента, сумма которых точно равна x - правильному решению?

Взято из введения в алгоритмы

Опишите алгоритм Θ (n lg n) -time что при множестве S из n целых чисел и другое целое число х, определяет, или нет, существуют два элемента в S чья сумма равна х.

Это мое лучшее решение, реализованное в 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 (2n lg n), поэтому он должен быть правильным.

Мой второй вопрос: есть ли лучшие решения? Является ли сортировка существенной?

4b9b3361

Ответ 1

Ваше решение кажется прекрасным. Да, вам нужно сортировать, потому что это предварительный вариант для двоичного поиска. Вы можете внести небольшую модификацию в свою логику следующим образом:

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

Ответ 2

Есть еще одно очень быстрое решение: представьте, что вам нужно решить эту проблему на Java примерно за 1 миллиард целых чисел. Вы знаете, что в целых числах Java от -2**31+1 до +2**31.

Создайте массив с 2**32 миллиардом бит (500 Мбайт, тривиальный для сегодняшнего оборудования).

Итерации по вашему набору: если у вас есть целое число, установите соответствующий бит в 1.

O (n) до сих пор.

Повторяйте снова по вашему набору: для каждого значения проверьте, установлен ли бит в "current val-x".

Если у вас есть, вы вернете true.

Конечно, для этого требуется 500 МБ памяти.

Но это будет работать вокруг любого другого решения O (n log n), если у вас есть, скажем, решение этой проблемы с 1 миллиардом целых чисел.

О (п).

Ответ 3

  • Это правильно; ваш алгоритм будет работать в O (n lg n) времени.

  • Существует лучшее решение: ваша логика вычисления diff неверна. Независимо от того, является ли a[i] больше или меньше, чем val, вам все равно нужно, чтобы diff был val - a[i].

Ответ 4

Здесь решение 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;
   }

Ответ 5

Я действительно думаю, что обнаружил незначительную ошибку в вашей реализации, но тестирование должно быстро выявить ее.

Подход выглядит действительным и достигнет желаемой производительности. Вы можете упростить его, заменив итеративный двоичный поиск на сканирование через массив, фактически заменив двоичный поиск линейным поиском, который возобновится там, где остановился предыдущий линейный поиск:

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) для сортировки слияния.

Ответ 6

Простое решение после сортировки перемещает указатели вниз с обоих концов массива, ища пары, которые суммируются с x. Если сумма слишком велика, уменьшите правый указатель. Если слишком низкий, увеличьте левый. Если указатели пересекаются, ответ будет отрицательным.

Ответ 7

Ваш анализ верен, и да, вы должны отсортировать массив, иначе бинарный поиск не будет работать.

Ответ 8

Вот альтернативное решение, добавив еще несколько условий в 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)