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

Найти все пары целых чисел в массиве, которые суммируются с заданным значением

Создайте алгоритм для нахождения всех пар целых чисел в массиве, которые суммируются с заданным значением.

Я пробовал эту проблему, используя хеш-таблицу для хранения записей для суммы элементов массива, но это не эффективное решение.

Какой алгоритм я могу использовать для эффективного решения?

4b9b3361

Ответ 1

Предположим, что требуемая сумма = R

  • сортировать массив
  • для каждого числа в массиве A (n), выполните двоичный поиск, чтобы найти число A (x) такое, что A (n) + A (x) = R

Ответ 2

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

Первое сканирование - поместить все элементы массива в хэш-таблицу - общее число O (n). Отдельные вставки только амортизируются O (1), но аккуратная вещь о том, как работает амортизационный анализ, означает, что O (n) является абсолютным - не амортизируется.

Второе сканирование - проверка (сумма - ток) в хеш-таблице - O (n) total.

Это превосходит методы сортировки и поиска O (n log n), по крайней мере теоретически.

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

for i in array.range
  hashset.insert (array [i])

  diff = sum - array [i]
  if hashset.includes (diff)
    output diff, array [i]

Если вам нужны позиции предметов, используйте хэш-карту и сохраните позиции позиции в ней. Если вам нужно справиться с дубликатами, вам может потребоваться хранить счета в хэш-карте. Для позиций и дубликатов вам может понадобиться хэш-карта стартовых указателей для связанных списков позиций.

Это делает предположения о реализации хеш-таблицы, но довольно безопасно, учитывая обычные реализации на большинстве современных языков и библиотек.

BTW - объединение сканирования не должно рассматриваться как оптимизация. Накладные расходы итерации должны быть незначительными. Проблемы с локальностью памяти могут сделать один проход немного более эффективным для очень больших массивов, но проблемы с реальной памятью в любом случае будут в хэш-таблице.

IMO - единственная реальная причина объединить сканирование - это потому, что вы хотите, чтобы каждая пара сообщалась один раз - обработка этого в двухскатном подходе была бы немного более сложной.

Ответ 3

  • Если массив отсортирован:

    Пусть я = 0, j = конец массива, sum = значение, которое вы ищете, затем выполните:

    Если я + j = sum, то вывод (i, j).
    Если я + j < sum, затем переместите я в нужное положение.
    Если я + j > sum, то переместите j в левую одну позицию.

    Сложность времени: O (n). Сложность пространства: O (1).

  • Если массив не отсортирован, есть несколько способов подойти к этой проблеме:

    • Сортируйте массив, а затем используйте приведенный выше подход.

    • HashMap:

      Сохраните все элементы в HashMap.

      a+b=sum, поэтому b=sum-a. Для каждого элемента a массива найдите b из HashMap.

      Поиск HashMap берет амортизацию O (1).

      Сложность времени: O (n). Сложность пространства: O (n).

    • BitMap:

      Итерируйте через вход, чтобы создать растровое изображение, где каждый бит соответствует значению элемента. Скажем, что вход {2,5,8}, затем мы переключаем индексы массива битмапа 2, 5 и 8 из двоичного числа 0 в 1. Это принимает O (1) на элемент, таким образом, O (n) в целом.

      Пройдите через вход снова. Мы знаем b=sum-a, поэтому для каждого элемента a на входе просмотрите его b, что можно сделать в O(1), так как это растровый индекс. Это также принимает O (n) в целом.

      Сложность времени: O (n) + O (n) = O (n). Космическая сложность: растровое пространство = O (n).

Ответ 4

Вам даже не нужно хранить все элементы в hashmap, а затем сканировать. Вы можете сканировать во время первой итерации.

void foo(int[] A, int sum) {
    HashSet<Integer> set = new HashSet<Integer>();
    for (int e : A) {
        if (set.contains(sum-e)) {
            System.out.println(e + "," + (sum-e));
            // deal with the duplicated case
            set.remove(sum-e);
        } else {
            set.add(e);
        }
    }
}

Ответ 5

Как насчет сортировки массива, а затем перехода с обоих концов?

Ответ 6

Если вы не против тратить O(M) в пространстве, где M - это сумма, которую вы ищете, вы можете сделать это в O(N + M) времени. Установите sums[i] = 1, когда i <= M за один проход за N, затем проверьте (sums[i] && sums[M-i]) на один проход поверх M/2.

Ответ 7

#include <iostream>
using namespace std;

#define MAX 15

int main()
{
 int array[MAX] = {-12,-6,-4,-2,0,1,2,4,6,7,8,12,13,20,24};
 const int find_sum = 0;
 int max_index = MAX - 1;
 int min_index = 0;
 while(min_index < max_index)
 {
  if(array[min_index] + array[max_index-min_index] == find_sum)
  {
   cout << array[min_index] << " & " << array[max_index-min_index] << " Matched" << endl;
   return 0;
  }
  if(array[min_index]+array[max_index-min_index] < find_sum)
  {
   min_index++;
   //max_index++;
  }
  if(array[min_index]+array[max_index-min_index] > find_sum)
  {
   max_index--;
  }
 }
 cout << "NO MATCH" << endl;
 return 0;
}
//-12 & 12 matched

Ответ 8

Реализовано в Python 2.7:

import itertools
list = [1, 1, 2, 3, 4, 5,]
uniquelist = set(list)
targetsum = 5
for n in itertools.combinations(uniquelist, 2):
    if n[0] + n[1] == targetsum:
    print str(n[0]) + " + " + str(n[1])

Выход:

1 + 4
2 + 3

Ответ 9

Вот решение, которое учитывает дубликаты записей. Он написан в javascript и предполагает, что массив отсортирован. Решение работает в O (n) времени и не использует никакой дополнительной памяти, кроме переменной.

var count_pairs = function(_arr,x) {
  if(!x) x = 0;
  var pairs = 0;
  var i = 0;
  var k = _arr.length-1;
  if((k+1)<2) return pairs;
  var halfX = x/2; 
  while(i<k) {
    var curK = _arr[k];
    var curI = _arr[i];
    var pairsThisLoop = 0;
    if(curK+curI==x) {
      // if midpoint and equal find combinations
      if(curK==curI) {
        var comb = 1;
        while(--k>=i) pairs+=(comb++);
        break;
      }
      // count pair and k duplicates
      pairsThisLoop++;
      while(_arr[--k]==curK) pairsThisLoop++;
      // add k side pairs to running total for every i side pair found
      pairs+=pairsThisLoop;
      while(_arr[++i]==curI) pairs+=pairsThisLoop;
    } else {
      // if we are at a mid point
      if(curK==curI) break;
      var distK = Math.abs(halfX-curK);
      var distI = Math.abs(halfX-curI);
      if(distI > distK) while(_arr[++i]==curI);
      else while(_arr[--k]==curK);
    }
  }
  return pairs;
}

Итак, это для всех.

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

Он учитывает только пары, но может быть переработан на

  • найти пары
  • найти пары < x
  • найти пары > x

Наслаждайтесь и не забудьте поднять его, если это лучший ответ!

Ответ 10

Решение, которое учитывает дубликаты и использует каждое число только один раз:

void printPairs(int[] numbers, int S) {
    // toMap(numbers) converts the numbers array to a map, where
    // Key is a number from the original array
    // Value is a count of occurrences of this number in the array
    Map<Integer, Integer> numbersMap = toMap(numbers); 

    for (Entry<Integer, Integer> entry : numbersMap.entrySet()) {
      if (entry.getValue().equals(0)) {
        continue;
      }
      int number = entry.getKey();
      int complement = S - number;
      if (numbersMap.containsKey(complement) && numbersMap.get(complement) > 0) {
      for (int j = 0; j < min(numbersMap.get(number), 
                              numbersMap.get(complement)); j++) {
        if (number.equals(complement) && numbersMap.get(number) < 2) {
           break;
        }
        System.out.println(number, complement);
        numbersMap.put(number, numbersMap.get(number) - 1);
        numbersMap.put(complement, numbersMap.get(complement) - 1);
      }
    }
  }
}

Ответ 11

Решение Hashtable в Ruby (довольно просто понять):

value = 100
pairs = [1,99,5,95]
hash_of_pairs = {}

pairs.map! do |pair|
  # Adds to hashtable the pair
  hash_of_pairs[pair] = pair

  # Finds the value the pair needs
  new_pair = hash_of_pairs[value - pair]

  # Returns the pair whenever the pair exists, or nil
  [new_pair, pair] if !new_pair.nil?
end.compact! # Cleans up the array, removing all nil values

print pairs # [[1,99], [5,95]]

Ответ 12

Мы можем использовать С++ STL-карту для решения этой проблемы

void subsetSum(int arr[], int n, int sum)
{
    map<int, int>Map;

    for(int i=0; i<n; i++)
    {
        Map[arr[i]]++;
        if(Map.count(sum-arr[i]))
        {
            cout<<arr[i]<<" "<<sum-arr[i]<<"\n";
        }
    }
}

Ответ 13

@Test
public void hasPairWithSum() {
  assertFalse(hasPairWithSum_Ordered_Logarithmic(new int[] { 1, 2, 3, 9 }, 8));
  assertTrue(hasPairWithSum_Ordered_Logarithmic(new int[] { 1, 2, 4, 4 }, 8));

  assertFalse(hasPairWithSum_Ordered_Linear(new int[] { 1, 2, 3, 9 }, 8));
  assertTrue(hasPairWithSum_Ordered_Linear(new int[] { 1, 2, 4, 4 }, 8));

  assertFalse(hasPairWithSum_Unsorted_Linear(new int[] { 9, 1, 3, 2 }, 8));
  assertTrue(hasPairWithSum_Unsorted_Linear(new int[] { 4, 2, 1, 4 }, 8));

  assertFalse(hasPairWithSum_Unsorted_Quadratic(new int[] { 9, 1, 3, 2 }, 8));
  assertTrue(hasPairWithSum_Unsorted_Quadratic(new int[] { 4, 2, 1, 4 }, 8));
}

private boolean hasPairWithSum_Ordered_Logarithmic(int[] data, int sum) {
  for (int i = 0; i < data.length; i++) {
    int current = data[i];
    int complement = sum - current;
    int foundIndex = Arrays.binarySearch(data, complement);
    if (foundIndex >= 0 && foundIndex != i) {
      return true;
    }
  }
  return false;
}

private boolean hasPairWithSum_Ordered_Linear(int[] data, int sum) {
  int low = 0;
  int high = data.length - 1;
  while (low < high) {
    int total = data[low] + data[high];
    if (total == sum) {
      return true;
    } else if (total < sum) {
      low++;
    } else {
      high--;
    }
  }
  return false;
}

private boolean hasPairWithSum_Unsorted_Linear(int[] data, int sum) {
  Set<Integer> complements = Sets.newHashSet();
  for (int current : data) {
    if (complements.contains(current)) {
      return true;
    }
    complements.add(sum - current);
  }
  return false;
}

private boolean hasPairWithSum_Unsorted_Quadratic(int[] data, int sum) {
  for (int i = 0; i < data.length; i++) {
    int current = data[i];
    int complement = sum - current;
    for (int j = 0; j < data.length; j++) {
      if (data[j] == complement && i != j) {
        return true;
      }
    }
  }
  return false;
}