O (n log (n)) алгоритм, который проверяет, является ли сумма 2 чисел в int [] = заданным числом - PullRequest
3 голосов
/ 14 ноября 2011

Я должен создать алгоритм O(n log(n)), который проверяет, является ли сумма 2 чисел в int [] == заданным числом.

например. Учитывая [1,4,7,2,3,4], будет сумма 8 (1 + 7), но не 20

В данном ответе предложены двоичная сортировка или сортировка слиянием, но они просто дали алгоритм сортировки слиянием без логической обработки этого конкретного требования. Тогда другой ответ был:

Предположим, что x это сумма, которую мы хотим проверить, z это набор элементов в этот массив: следующий алгоритм решает проблему:

  1. Сортировка элементов в S.
  2. Формируем множество S ’= {z: z = x - y для некоторого y ∈ S}.
  3. Сортировать элементы по S '.
  4. Если какое-либо значение в S появляется более одного раза, удалите все, кроме одного экземпляра. Сделайте то же самое для S ’.
  5. Объединить два отсортированных набора S и S ’.
  6. Существует два элемента в S, сумма которых равна x тогда и только тогда, когда одно и то же значение появляется в последовательных позициях в объединенном выводе.

Чтобы обосновать претензию на шаге 4, сначала обратите внимание, что если есть какое-либо значение появляется дважды в объединенном выводе, он должен появляться последовательно позиции. Таким образом, мы можем переформулировать условие на шаге 5, если оно существует два элемента в S, сумма которых в точности равна x тогда и только тогда, когда одно и то же значение появляется дважды в объединенном выводе. Предположим, что появляется некоторое значение w дважды. Затем w появился один раз в S и один раз в S ’. Потому что ш появился в S ’, существует такой y ∈ S, что w = x - y или x = w + y. С ж ∈ S, элементы w и y находятся в S и суммируются с x.

Наоборот, предположим, что существуют такие значения w, y ∈ S, что w + y = Икс. Тогда, поскольку x - y = w, значение w появляется в S ’. Таким образом, w находится в и S, и S ’, и поэтому он будет отображаться дважды в объединенном выводе.

Шаги 1 и 3 требуют O (n log n) шагов. Шаги 2, 4, 5 и 6 требуют O (n) шагов. Таким образом, общее время работы O (n log n).

Но я не совсем понимаю, что они имели в виду. На шаге 2, что такое х и у?

Но я создал свой собственный ниже, интересно, если это O(n log(n))?

class FindSum {

  public static void main(String[] args) {
    int[] arr = {6,1,2,3,7,12,10,10};
    int targetSum = 20;

    Arrays.sort(arr);
    System.out.println(Arrays.toString(arr));
    int end = arr.length - 1;
    if (FindSum.binarySearchSum(arr, targetSum, 0, end, 0, end)) {
      System.out.println("Found!");
    } else {
      System.out.println("Not Found :(");
    }
  } 

  public static boolean binarySearchSum(int[] arr, int targetSum,
                                        int from1, int end1,
                                        int from2, int end2) {
    // idea is to use 2 "pointers" (simulating 2 arrays) to (binary) search 
    // for target sum
    int curr1 = from1 + (end1-from1)/2;
    int curr2 = from2 + (end2-from2)/2;
    System.out.print(String.format("Looking between %d to %d, %d to %d: %d, %d", from1, end1, from2, end2, curr1, curr2));
    int currSum = arr[curr1] + arr[curr2];
    System.out.println(". Sum = " + currSum);

    if (currSum == targetSum) { 
      // base case
      return true;
    } else if (currSum > targetSum) { 
      // currSum more than targetSum
      if (from2 != end2) { 
        // search in lower half of 2nd "array"
        return FindSum.binarySearchSum(arr, targetSum, from1, end1, from2, curr2 - 1);
      } else if (from1 != end2) { 
        // search in lower half of 1st "array" (resetting the start2, end2 args)
        return FindSum.binarySearchSum(arr, targetSum, from1, curr1 - 1, 0, arr.length - 1);
      } else {
        // can't find
        return false;
      }
    } else {
      // currSum < targetSum
      if (from2 != end2) { 
        // search in upper half of 2nd "array"
        return FindSum.binarySearchSum(arr, targetSum, from1, end1, curr2 + 1, end2);
      } else if (from1 != end2) { 
        // search in upper half of 1st "array" (resetting the start2, end2 args)
        return FindSum.binarySearchSum(arr, targetSum, curr1 + 1, end1, 0, arr.length - 1);
      } else {
        // can't find
        return false;
      }
    }
  }

}

Ответы [ 5 ]

5 голосов
/ 14 ноября 2011

Аналогично @ user384706, однако вы можете сделать это с помощью O(n).

Они говорят следующее: S = [1,4,7,2,3,4]

Добавьте их в HashSet, в идеале TIntHashSet (но сложность по времени та же)

int total = 9;
Integer[] S = {1, 4, 7, 2, 3, 4, 6};
Set<Integer> set = new HashSet<Integer>(Arrays.asList(S));
for (int i : set)
    if (set.contains(total - i))
        System.out.println(i + " + " + (total - i) + " = " + total);

печатает

2 + 7 = 9
3 + 6 = 9
6 + 3 = 9
7 + 2 = 9
4 голосов
/ 14 ноября 2011

Они говорят следующее:
S=[1,4,7,2,3,4]

Сортировать S, используя mergesort, вы получите Ss=[1,2,3,4,7].Стоимость составляет O(nlogn) - просто проверьте вики для этого.

Тогда у вас есть x=8
Итак, вы формируете S'=[7,6,5,4,1], вычитая x с элементами в S.
Сортировка S' с использованием слияния в O(nlogn)
Для удаления дубликатов требуется O(n).

Затем вы объединяете Ss и S'.
Вы проверяете наличие дубликатов в последовательных позициях в O(n).
Всего получается:
O(nlogn)+O(nlogn)+O(n)+O(n)+O(n) = O(nlogn)

3 голосов
/ 14 ноября 2011

А как насчет O (n) решения?

Из вашего вопроса не ясно, должны ли вы использовать то, что вы назвали "другим ответом" [sic], или вы можете придумать собственное решение.

Первое, что вы должны спросить: «Каковы требования?» Потому что есть ограничения. Какое максимальное число целых чисел вы получите? Два миллиона? Десять миллионов? Каков диапазон этих целых чисел? В вашем вопросе они всегда кажутся больше нуля. Какое максимальное значение могут иметь эти целые числа? Сколько памяти вы можете использовать?

Поскольку всегда компромиссы.

Например, вот неоптимизированное (см. Ниже) O (n) решение вашей проблемы (я добавил «8» к вашему входу):

@Test
public void testIt() {
    final int max = 10000000;
    final int[] S = new int[max+1];
    final int[] in = { 1, 4, 3, 2, 4, 7, 8 };
    for ( final int i : in ) {
        S[i]++;
    }
    assertFalse( containsSum(S, 1) );
    assertFalse( containsSum(S, 2) );
    assertTrue( containsSum(S, 3) );
    assertTrue( containsSum(S, 4) );
    assertTrue( containsSum(S, 5) );
    assertTrue( containsSum(S, 6) );
    assertTrue( containsSum(S, 7) );
    assertTrue( containsSum(S, 8) );
    assertTrue( containsSum(S, 9) );
    assertTrue( containsSum(S, 10) );
    assertTrue( containsSum(S, 11) );
    assertFalse( containsSum(S, 13) );
    assertFalse( containsSum(S, 14) );
    assertTrue( containsSum(S, 12) );
    assertTrue( containsSum(S, 15) );
    assertFalse( containsSum(S, 16) );
}

private static boolean containsSum( final int[] ar, final int sum ) {
    boolean found = false;
    for (int i = 1; i < sum && !found; i++) {
            final int b = sum - i;
            found = i == b ? ar[i] > 1 : ar[i] > 0 && ar[b] > 0;
    }
    return found;
}

Это неоптимизировано, так как легко написать O (n) , работающий для целых чисел от 0 до 2 ** 31, используя «только» 1 ГБ памяти (где вы представляли бы свои S и ваш S 'использует биты вместо целых, как я сделал здесь).

Конечно, можно подумать «но 1 ГБ - это много памяти» : но все зависит от требований. Мое решение выше (или его оптимизированная версия) - O (n) , и оно может решить ввод, скажем, из 100 миллионов целых чисел за мгновение, когда любое другое решение не сработает (потому что вы ошибки OutOfMemory из-за издержек Java-объектов).

Первое, что нужно спросить: «Каковы требования?» . Вам нужно больше информации о входе, потому что это всегда компромисс.

0 голосов
/ 04 апреля 2018

Работает следующим образом:

  1. Сортировка входного массива
  2. создать две переменные front = 0 [начальная точка] сзади = длина массива [конечная точка].
  3. Начинается с обоих концов массива, вычисляет сумму, если его с нулевым приращением как спереди, так и сзади
  4. если не увеличивать сторону, которая содержит больший элемент. [Так как массив отсортирован, нет шансов найти элемент такой, что их сумма равна 0]
  5. остановка, когда впереди> = сзади.

public class TwoSumFaster {

private static int countTwoSum(int[] numbers) {
    int count = 0;
    int front = 0, rear = numbers.length - 1;

    while (front < rear) {
        if (numbers[front] + numbers[rear] == 0) {
            front++;
            rear--;
            count++;
        } else {
            if (Math.abs(numbers[front]) > Math.abs(numbers[rear])) {
                front++;
            } else {
                rear--;
            }
        }
    }

    return count;
}

public static void main(String[] args) {
    int[] numbers = { 1, 3, 5, 7, 12, 16, 19, 15, 11, 8, -1, -3, -7, -8, -11, -17, -15 };
    Arrays.sort(numbers); 
    System.out.println(countTwoSum(numbers));
}

}

0 голосов
/ 14 ноября 2011

Ваш алгоритм O (n log n). Каждый раз вы либо делите первый размер массива на два, либо выполняете двоичный поиск по второму. Это O ((log n) ^ 2) наихудший случай (т. Е. S = {1,2 ..., n} и x = 0), и поэтому он поглощается сортировкой.

В любом случае вы можете сделать это немного проще в O (n log n):

  1. сортировка массива (O (n log n))
  2. итерация его элементов (O (n)), в то время как в каждой итерации двоичный поиск (O (log n)) X-дополнения текущего элемента.

редактирование: В ответ на ваш первый вопрос. х - сумма, которую вы ищете, а у - элементы входного набора. Поэтому, если S = ​​{y_1, y_2, y_3, ..., y_n}, то S '= {x - y_1, x - y_2, x - y_3, ... x - y_n}

...