web-dev-qa-db-ja.com

与えられた合計に加算する配列の数値のペアを見つける

質問:正の整数の並べ替えられていない配列が与えられた場合、その配列から合計が所定の合計になる整数のペアを見つけることは可能ですか?

制約:これは、O(n)およびインプレース(配列、ハッシュマップなどの外部ストレージなし))で実行する必要があります(追加の変数/ポインターを使用できます)

これが不可能な場合、同じことの証明がありますか?

31
noMAD

ソートされた配列がある場合、2つのポインターを中央に移動することにより、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)数値のサイズに制限がある場合(または配列が最初に既にソートされている場合)。要因は本当に小さく、私はそれを削る気にしたくない。

証明:

(i*, j*)に解決策がある場合、一般性を失うことなく、ii*に到達する前にjj*に到達するとします。 j'jの間のすべてのj*について、a[j'] > a[j*]を推定できるため、そのa[i] + a[j'] > a[i*] + a[j*] = targetを推定できるため、アルゴリズムの以下のすべてのステップがjがj*(または等しい値)に達するまで、iに前進してソリューションを「ミス」する機会を与えずに減少させます。

他の方向の解釈も同様です。

53
hugomg

ソートされた配列で機能するO(N) timeおよびO(1) spaceソリューション:

Mを後の値とします。 XYの2つのポインターを使用します。最初に_X=0_を開始し、最後に_Y=N-1_を開始します。合計_sum = array[X] + array[Y]_を計算します。 _sum > M_の場合、Yをデクリメントし、そうでない場合はXをインクリメントします。ポインターが交差する場合、ソリューションは存在しません。

一般的な配列に対してこれを取得するために適切な場所に並べ替えることができますが、一般的にO(N) timeおよびO(1) spaceソリューションがあるかどうかはわかりません。

12
PengOne

@PengOneが述べたように、それは物事の一般的なスキームでは不可能です。ただし、i/pデータに何らかの制限を加える場合。

  1. すべての要素はすべて+またはすべて-です。そうでない場合、範囲(高、低)を知り、変更を加える必要があります。
  2. K、2つの整数の合計は、一般の要素と比較してまばらです。
  3. I/p配列A [N]を破壊しても問題ありません。

ステップ1:SUM未満のすべての要素を配列の先頭に移動します。たとえば、Nパスでは、[0、K]と[K、N-1]に配列を分割し、[0、K]に要素<= SUMが含まれるようにします。

ステップ2:境界(0〜SUM)がわかっているため、基数ソートを使用できます。

ステップ3:A [K]でバイナリ検索を使用します。補完的な要素を見つける必要がある場合、配列A [K]の半分を見るだけでよいのが良い点です。 A [k]では、A [0 to K/2 + 1]を反復処理します。A[i to K]でバイナリ検索を行う必要があります。

合計約。時間は、N + K + K/2 lg(K)で、Kはi/p A [N]の0からSumまでの要素数

注:@PengOneのアプローチを使用する場合、Kでステップ3を実行できます。したがって、合計時間はN + 2Kになり、これは間違いなくO(N)です。

追加のメモリは使用しませんが、i/p配列を破棄します。これは、開始する順序がなかったため、やはり悪くはありません。

3
d1val

Java(Time Complexity 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]));
        }
    }
}
}
3
Chetan Kole

これは、配列に数値が含まれており、その上限が事前にわかっている場合に可能です。次に、カウントソートまたは基数sort(o(n))を使用し、@ PengOneが提案したアルゴリズムを使用します。

そうでなければ、O(n) solution.But O(nlgn)ソリューションはこのように動作します:-

まず、マージソートまたはクイックソート(インプレース)を使用して配列をソートします。このソートされた配列にsum-array_elementがあるかどうかを調べます。そのためにバイナリ検索を使用できます。

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

まず、 radix sort を使用して配列をソートします。 O(kN)に戻ります。その後、@ PengOneが示唆するとおりに進みます。

2
Miquel

O(N)アルゴリズムです。これは in-place O(N)重複除去アルゴリズム に依存しています。配列内のintに対する適切なハッシュ関数の存在。

最初に、配列からすべての重複を削除します。

次に、配列を調べて、各数値xをmin(x、S-x)に置き換えます。ここで、Sは到達したい合計です。

3番目に、配列に重複があるかどうかを確認します。「x」が重複している場合、「x」と「S-x」は元の配列で発生しているはずで、ペアが見つかりました。

2
Paul Hankin

次のサイトでは、ハッシュセットを使用して番号を確認し、指定された合計現在の番号をハッシュセットで検索する簡単なソリューションを提供しています http://www.dsalgo.com/UnsortedTwoSumToK.php

  1. Count sortを使用して、配列O(n)をソートします。
  2. 配列の0番目のインデックスから始まる2つのポインターと、配列の終わりから別の2つのポインターを取得します(n-1)。

    低<=高になるまでループを実行します

    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)になります。

2
Jay

Javascriptの場合:このコードは、nがより大きい場合、繰り返しの回数と回数が増加します。 n ] === 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));        
1
EJISRHA

これは、魔女が重複エントリを考慮するソリューションです。 JavaScriptで記述され、ソートされた配列とソートされていない配列を使用して実行されます。ソリューションは、O(n) timeで実行されます。

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を見つける
  • ペアを見つける> x

楽しい!

1
dRoneBrain

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
1
SuperNova

私はインタビューの中でこれと同じ質問をされましたが、これは私が念頭に置いていたスキームです。負の数を許可するために、改善する必要がありますが、必要なのはインデックスを変更することだけです。空間的には良くありませんが、ここでの実行時間は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”);
 }
}

批評家を歓迎します。

0
Jorge Alcantara

Javaでは、これは配列の最大数に依存します。 2つの要素のインデックスを持つ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;
}
0
Bruce Zu

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
0
vasilenicusor

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();
}
0
Igor Nemtsev
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のソリューション

0
sim

可能になるとは限りません。指定された合計はどのように選択されますか?

例:整数のソートされていない配列

2, 6, 4, 8, 12, 10

与えられた合計:

7

0
user732933

最初に逆配列=> sum-実際の配列を見つけてから、これらの新しい配列の要素が実際の配列に存在するかどうかを確認します。

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);
0
Coşkun Deniz