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

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

ограничения: это должно быть сделано в O (n) и на месте (без какого-либо внешнего хранилища, такого как массивы, хэш-карты) (вы можете использовать дополнительные переменные/указатели)

Если это невозможно, Может ли быть доказательство того же самого?

19 ответов


если у вас есть отсортированный массив, вы можете найти такую пару в O (n), переместив два указателя к середине

i = 0
j = n-1
while(i < j){
   if      (a[i] + a[j] == target) return (i, j);
   else if (a[i] + a[j] <  target) i += 1;
   else if (a[i] + a[j] >  target) j -= 1;
}
return NOT_FOUND;

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

доказательство:

если есть решение (i*, j*) предположим, без потери общности, это i достигает i* до j достигает j*. С тех пор для всех j' между j* и j известно, что a[j'] > a[j*] мы можем экстраполировать, что a[i] + a[j'] > a[i*] + a[j*] = target и, следовательно, все следующие шаги алгоритма приведут к уменьшению j до тех пор, пока он не достигнет j* (или равное значение) без указания i шанс продвинуться вперед и "Мисс" решение.

интерпретация в другом направлении аналогична.


An O(N) время и O(1) пространственное решение, которое работает с отсортированным массивом:

пусть M быть значение, которое вы после. Используйте два указателя,X и Y. Старт X=0 в начале и Y=N-1 в конце. Вычислить сумму sum = array[X] + array[Y]. Если sum > M, затем декремент Y, в противном случае increment X. Если указатели пересекаются, то решения не существует.

вы можете отсортировать на месте, чтобы получить это для общего массива, но я не уверен, что есть O(N) время и O(1) решение пространства в целом.


это возможно, если массив содержит числа, верхний предел которых известен вам заранее. Затем используйте сортировку подсчета или сортировку radix(o (n)) и используйте алгоритм, предложенный @PengOne.

в противном случае Я не могу придумать решение O(n).Но решение O(nlgn) работает следующим образом:-

сначала отсортируйте массив с помощью сортировки слиянием или быстрой сортировки (для inplace). Найдите, есть ли sum - array_element в этом отсортированном массиве. Можно использовать двоичный поиск что.

So total time complexity: O(nlgn) + O(lgn) -> O(nlgn).

как отметил @PengOne, это невозможно в общей схеме вещей. Но если вы сделаете некоторые ограничения на данные ввода-вывода.

  1. все элементы все + или все -, если нет, то нужно будет знать диапазон (высокий, низкий) и внести изменения.
  2. K, сумма двух целых чисел разрежена по сравнению с элементами в целом.
  3. это нормально, чтобы уничтожить I / P массив A[N].

Шаг 1: переместите все элементы меньше суммы в начало массива, скажем, в N проходах мы разделили массив на [0,K] & [K, N-1] такой,что [0, K] содержит элементы

Шаг 2: поскольку мы знаем границы (от 0 до суммы), мы можем использовать сортировку radix.

Шаг 3: Используйте двоичный поиск на[K], хорошо, что если нам нужно найти дополнительный элемент, нам нужно посмотреть только половину массива A[K]. поэтому в A[k] мы перебираем a[ 0 to K/2 + 1] Нам нужно выполнить двоичный поиск в A[i to K].

Так что всего appx. Время, N + K + K/2 lg (K), где K-количество элементов 0 для суммирования в I / p A[N]

Примечание: Если вы используете подход @PengOne, вы можете сделать шаг 3 в K. таким образом, общее время будет N+2K, что определенно O (N)

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


во-первых, отсортируйте массив с помощью сортировка radix. Это отбросит вас назад O (kN). Затем переходите в @PengOne предлагаю.


следующий сайт дает простое решение, используя hashset, который видит число, а затем ищет hashset для заданной суммы-текущего числа http://www.dsalgo.com/UnsortedTwoSumToK.php


вот алгоритм O(N). Он полагается на алгоритм удаления дубликатов на месте O(N), и существование хорошей хэш-функции для ints в вашем массиве.

Сначала удалите все дубликаты из массива.

во-вторых, пройдите через массив и замените каждое число x на min (x, S-x), где S-сумма, которую вы хотите достичь.

В-третьих, найдите, есть ли дубликаты в массиве: если " x " дублируется, то "x" и "S-x" должны иметь произошел в исходном массиве, и вы нашли свою пару.


мое решение в Java (временная сложность O (n)), это выведет все пары с заданной суммой

import java.util.HashMap;
import java.util.Map;

public class Test {
public static void main(String[] args) {
    // TODO Auto-generated method stub
    Map<Integer, Integer> hash = new HashMap<>();
    int arr[] = {1,4,2,6,3,8,2,9};
    int sum = 5;
    for (int i = 0; i < arr.length; i++) {
        hash.put(arr[i],i);
    }

    for (int i = 0; i < arr.length; i++) {
        if(hash.containsKey(sum-arr[i])){
            System.out.println(i+ " " +  hash.get(sum-arr[i]));
        }
    }
}
}

  1. используйте count sort для сортировки массива O (n).
  2. возьмите два указателя, один из которых начинается с 0-го индекса массива, а другой-с конца массива (n-1).

    запустите цикл до low

    Sum = arr[low] + arr[high]  
    if(sum == target)
           print low, high
    if(sum < target)
           low++
    if(sum > target)
           high--
    

    Шаг 2 до 10 занимает O(n) время, а подсчет сортировки занимает O (n). Таким образом, общая сложность времени будет O(n).


вот решение ведьма учитывает повторяющиеся записи. Он написан на javascript и работает с использованием отсортированных и несортированных массивов. Решение выполняется в O (n) раз.

var count_pairs_unsorted = function(_arr,x) {
  // setup variables
  var asc_arr = [];
  var len = _arr.length;
  if(!x) x = 0;
  var pairs = 0;
  var i = -1;
  var k = len-1;
  if(len<2) return pairs;
  // tally all the like numbers into buckets
  while(i<k) {
    asc_arr[_arr[i]]=-(~(asc_arr[_arr[i]]));
    asc_arr[_arr[k]]=-(~(asc_arr[_arr[k]]));
    i++;
    k--;
  }
  // odd amount of elements
  if(i==k) {
    asc_arr[_arr[k]]=-(~(asc_arr[_arr[k]]));
    k--;
  }
  // count all the pairs reducing tallies as you go
  while(i<len||k>-1){
    var y;
    if(i<len){
      y = x-_arr[i];
      if(asc_arr[y]!=undefined&&(asc_arr[y]+asc_arr[_arr[i]])>1) {
        if(_arr[i]==y) {
          var comb = 1;
          while(--asc_arr[_arr[i]] > 0) {pairs+=(comb++);}
        } else pairs+=asc_arr[_arr[i]]*asc_arr[y];
        asc_arr[y] = 0;
        asc_arr[_arr[i]] = 0;
      }

    }
    if(k>-1) {
      y = x-_arr[k];
      if(asc_arr[y]!=undefined&&(asc_arr[y]+asc_arr[_arr[k]])>1) {
        if(_arr[k]==y) {
          var comb = 1;
          while(--asc_arr[_arr[k]] > 0) {pairs+=(comb++);}
        } else pairs+=asc_arr[_arr[k]]*asc_arr[y];
        asc_arr[y] = 0;
        asc_arr[_arr[k]] = 0;
      }

    }
    i++;
    k--;
  }
  return pairs;
}

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

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

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

наслаждайтесь!


реализация Ruby

ar1 = [ 32, 44, 68, 54, 65, 43, 68, 46, 68, 56]
for i in 0..ar1.length-1
 t  = 100 - ar1[i]
  if ar1.include?(t)
    s= ar1.count(t)
    if s < 2
      print   s , " - " ,  t, " , " , ar1[i]  , " pair ", i, "\n"
    end
  end
end

в javascript: этот код, когда n больше, то время и количество итераций увеличиваются. Количество тестов, выполненных программой, будет равно ((n*(n/2)+n/2), где n-количество элементов.Заданное число суммы отбрасывается в if (arr[i] + arr[j] = = = 0), где 0 может быть любым заданным числом.

var arr = [-4, -3, 3, 4];          
                var lengtharr = arr.length;        
                var i = 0;                         
                var j = 1;                         
                var k = 1;                          
                do {                                                    
                    do {
                        if (arr[i] + arr[j] === 0) { document.write(' Elements arr [' + i + '] [' + j + '] sum 0'); } else { document.write('____'); }
                     j++;
                    } while (j < lengtharr);        
                    k++;
                    j = k;
                    i++;
                } while (i < (lengtharr-1));        

не гарантируется возможность; как выбрана данная сумма?

пример: несортированный массив целых чисел

2, 6, 4, 8, 12, 10

данную сумму:

7

??


вот решение в python:

a = [9, 8, 9, 2, 15, 11, 21, 8, 9, 2, 2, 8, 9, 2, 15, 11, 21, 8, 9, 2, 9, 8, 9, 2, 15, 11, 21, 8, 9, 2, 2, 8, 9, 2, 15, 11, 2, 8, 9, 2, 2, 8,
     9, 2, 15, 11, 21, 8, 9, 12, 2, 8, 9, 2, 15, 11, 21, 7, 9, 2, 23, 8, 9, 2, 15, 11, 21, 8, 9, 2, 2, 12, 9, 2, 15, 11, 21, 8, 9, 2, 2,
     8, 9, 2, 15, 11, 21, 8, 9, 2, 2, 8, 9, 2, 15, 11, 21, 8, 9, 2, 2, 8, 9, 2, 15, 11, 21, 8, 9, 2, 2, 7.12, 9, 2, 15, 11, 21, 8, 9, 2, 2, 8, 9,
     2, 15, 11, 21, 8, 9, 2, 2, 8, 9, 2, 15, 11, 21, 8, 9, 2, 2, 8, 9, 2, 15, 11, 21, 8, 9, 2, 2, 8, 9, 2, 15, 11, 21, 8, 0.87, 78]
i = 0
j = len(a) - 1
my_sum = 8
finded_numbers = ()
iterations = 0
while(OK):
    iterations += 1
    if (i < j):
        i += 1
    if (i == j):
        if (i == 0):
            OK = False
            break
        i = 0
        j -= 1
    if (a[i] + a[j] == my_sum):
        finded_numbers = (a[i], a[j]) 
        OK = False
print finded_numbers
print iterations

мне задали этот же вопрос во время интервью, и это схема, которую я имел в виду. Осталось сделать улучшение, чтобы разрешить отрицательные числа, но было бы необходимо только изменить индексы. С точки зрения пространства это не хорошо, но я считаю, что время работы здесь будет O(N)+O(N)+O(подмножество N) -> O(N). Возможно, я ошибаюсь.

void find_sum(int *array_numbers, int x){
 int i, freq, n_numbers; 
 int array_freq[x+1]= {0}; // x + 1 as there could be 0’s as well
 if(array_numbers)
 {
  n_numbers = (int) sizeof(array_numbers);
  for(i=0; i<n_numbers;i++){ array_freq[array_numbers[i]]++; } //O(N) 
  for(i=0; i<n_numbers;i++) 
  { //O(N) 
   if ((array_freq[x-array_numbers[i]] > 0)&&(array_freq[array_numbers[i]] > 0)&&(array_numbers[i]!=(x/2)))
   { 
    freq = array_freq[x-array_numbers[i]] * array_freq[array_numbers[i]];
    printf(“-{%d,%d} %d times\n”,array_numbers[i],x-array_numbers[i],freq ); 
    // “-{3, 7} 6 times” if there’s 3 ‘7’s and 2 ‘3’s
    array_freq[array_numbers[i]]=0;
    array_freq[x-array_numbers[i]]=0; // doing this we don’t get them repeated
   }
  } // end loop
  if ((x%2)=0)
  {
   freq = array_freq[x/2];
   n_numbers=0;
   for(i=1; i<freq;i++)
   { //O([size-k subset])
    n_numbers+= (freq-i); 
   } 
   printf(“-{%d,%d} %d times\n”,x/2,x/2,n_numbers);
  }
  return;
 }else{
 return; // Incoming NULL array 
 printf(“nothing to do here, bad pointer\n”);
 }
}
критики.

в java это зависит от максимального числа в массиве. он возвращает int [], имеющий индексы двух элементов. Это O (N).

  public static int[] twoSum(final int[] nums, int target) {
    int[] r = new int[2];
    r[0] = -1;
    r[1] = -1;
    int[] vIndex = new int[0Xffff];
    for (int i = 0; i < nums.length; i++) {
        int delta = 0Xfff;
        int gapIndex = target - nums[i] + delta;
        if (vIndex[gapIndex] != 0) {
            r[0] = vIndex[gapIndex];
            r[1] = i + 1;
            return r;
        } else {
            vIndex[nums[i] + delta] = i + 1;
        }
    }
    return r;
}

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

const arr = [0, 1, 2, 6];

const sum = 8;

let isPairExist = arr
  .map(item => sum - item) // [8, 7, 6, 2];
  .find((item, index) => {
    arr.splice(0, 1); // an element should pair with another element
    return arr.find(x => x === item);
  })
  ? true : false;

console.log(isPairExist);

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

void TwoIntegersSum(int[] given, int sum)
{
    Hashtable ht = new Hashtable();
    for (int i = 0; i < given.Length; i++)
        if (ht.Contains(sum - given[i]))
            Console.WriteLine("{0} + {1}", given[i], sum - given[i]);
        else
            ht.Add(given[i], sum - given[i]);
    Console.Read();
}

def pair_sum(arr,k):
    counter = 0
    lookup = set()
    for num in arr:
        if k-num in lookup:
            counter+=1
        else:
            lookup.add(num)
    return counter
    pass
pair_sum([1,3,2,2],4)

решение в python