重複のない配列マージ


15

私は最近、2つの配列マージし、重複を削除するためのStackOverflowでこのJavascriptコードを見ました:

Array.prototype.unique = function() {
    var a = this.concat();
    for(var i=0; i<a.length; ++i) {
        for(var j=i+1; j<a.length; ++j) {
            if(a[i] === a[j])
                a.splice(j--, 1);
        }
    }
    return a;
};

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = array1.concat(array2).unique(); 

このコードは機能しますが、ひどく非効率的です(O(n^2))。あなたの課題は、複雑さの少ないアルゴリズムを作成することです。

勝利の基準は、最も複雑さの少ないソリューションですが、文字の最短の長さによって関係が壊れます。

要件

「正確さ」に関する次の要件を満たす関数にすべてのコードをまとめてパッケージ化します。

  • 入力:2つの配列
  • 出力:1つの配列
  • 両方の配列の要素を一緒にマージします-いずれかの入力配列の要素はすべて出力配列に含まれている必要があります。
  • 出力された配列には重複がないはずです。
  • 順序は関係ありません(オリジナルとは異なります)
  • すべての言語が重要
  • 標準ライブラリの配列関数を使用して、一意性を検出したり、セット/配列をマージしたりしないでください(ただし、標準ライブラリの他のものは大丈夫です)。配列の連結は問題ありませんが、上記のすべてを既に実行している関数は違います。

配列関数を使用せずに配列を作成または追加するにはどうすればよいですか?
エミールヴィクストローム14年

@EmilVikström編集をご覧ください。配列の一意性関数を使用できないことを意味しました。不明確で申し訳ありません。
hkk 14年

配列の1つに重複がある場合、それらも削除しますか?たとえば、マージし[1, 2, 2, 3][2, 3, 4]リターンする[1, 2, 2, 3, 4]必要があり[1, 2, 3, 4]ますか?
OI 14年

1
@OIはい、それは簡単すぎます。
hkk 14年

1
私は尋ねることができます:何の配列?単純に整数または文字列を想定できますか、またはマルチレベルオブジェクトのようなより複雑なものを許可する必要がありますか?
jawns317 14年

回答:


8

Perl

27文字

単純なPerlハック

my @vals = ();
push @vals, @arr1, @arr2;
my %out;
map { $out{$_}++ } @vals;
my @unique = keys %out;

私は誰かがこれをワンライナーできると確信しています..したがって(Dom Hastingsに感謝)

sub x{$_{$_}++for@_;keys%_}

1
「標準ライブラリの配列関数を使用して一意性を検出しないでください(ただし、標準ライブラリのその他の要素は問題ありません)」
ジョン・ドヴォラック14年

1
どのようにその規則に違反していますか?独自の機能を使用していませんか?
ザックレイトン14年

では、どのように機能しますか?すみません、perlを読むことができません。ハッシュマップのキーを読み取る場合-そのルールでそれはOKとしてカウントされますか?そうだと確信するまで投票しません。
ジョンドヴォルザーク14年

1
配列を結合し、両方をループし、配列ループの現在の値がキーである値を増分するハッシュに追加します。次に、そのハッシュのキーを使用します。これは私の作業の一部で使用しています。したがって、[1,1,2,3,4,4]は{1 => 2、2 => 1、3 => 1になります。 、4 => 2}
ザックレイトン14年

あなたは27文字になるようにコードを短縮することができます@ZachLeighton sub x{$_{$_}++for@_;keys%_}:(!それはタイに降りてくる場合)ととして使用するz((1,2,3,4),(2,3,4,5,6))
ドムヘイスティングス

10

JavaScriptのO(N)131 124 116 92(86?)

ゴルフバージョン:

function m(i,x){h={};n=[];for(a=2;a--;i=x)i.map(function(b){h[b]=h[b]||n.push(b)});return n}

人間が読めるゴルフバージョン:

function m(i,x) {
   h = {}
   n = []
   for (a = 2; a--; i=x)
      i.map(function(b){
        h[b] = h[b] || n.push(b)
      })
   return n
}

私はconcatそのように使用して86文字でそれを行うことができます:

function m(i,x){h={};n=[];i.concat(x).map(function(b){h[b]=h[b]||n.push(b)});return n}

:それはこのJsPerfに基づく静止O(N)である場合しかし、私はわからないhttp://jsperf.com/unique-array-merging-concat-vs-looping、連結バージョンはわずかに速く小さなアレイではなく、より遅いとされてより大きなアレイ(Chrome 31 OSX)。

実際にはこれを行います(ゴルフは悪い習慣でいっぱいです):

function merge(a1, a2) {
   var hash = {};
   var arr = [];
   for (var i = 0; i < a1.length; i++) {
      if (hash[a1[i]] !== true) {
        hash[a1[i]] = true;
        arr[arr.length] = a1[i];
      }
   }
   for (var i = 0; i < a2.length; i++) {
      if (hash[a2[i]] !== true) {
        hash[a2[i]] = true;
        arr[arr.length] = a2[i];
      }
   }
   return arr;
}
console.log(merge([1,2,3,4,5],[1,2,3,4,5,6]));

私は複雑なコンピューティングは得意ではありませんが、そうだと思いますO(N)。誰かが明確にすることができれば大好きです。

編集:これは、任意の数の配列を取り、それらをマージするバージョンです。

function merge() {
   var args = arguments;
   var hash = {};
   var arr = [];
   for (var i = 0; i < args.length; i++) {
      for (var j = 0; j < args[i].length; j++) {
        if (hash[args[i][j]] !== true) {
          arr[arr.length] = args[i][j];
          hash[args[i][j]] = true;
        }
      }
    }
   return arr;
}
console.log(merge([1,2,3,4,5],[1,2,3,4,5,6],[1,2,3,4,5,6,7],[1,2,3,4,5,6,7,8]));

これは、私が数秒で投稿しようとしていたものとほぼ同じです: JSについて)
EmilVikström14年

@EmilVikströmそれに感謝します。JavaScriptはあると信じていますが、その証拠はありません。速い指を持っていることをおcomments

これは素晴らしいアプローチです。ただし、適切にフォーマットされたバージョンに加えて、「コードゴルフ」スタイルのソリューションを提供することはできますか?複数の人々がこれを正しいアプローチと考えていることを見て、おそらくでのタイになるでしょうO(N)
hkk 14年

@ cloudcoder2000わかりました。コードゴルフバージョンは実際にはあまり効率的でない可能性があるため、フルバージョンを印刷したかったのです。
ジョージリース14年

1
@ cloudcoder2000完全に独立しているわけではないため、最悪の場合はそうではありませんO(A*B)N混乱を招くため使用しません)。すべての入力配列(すべてABが実際と同じ量の要素()を持っている場合、すべての配列入力の要素数として定義するときと同じO(SUM(B) FOR ALL A)ように書き換えることができます。O(N)N
meiamsome 14年

4

Python 2.7、38文字

F=lambda x,y:{c:1 for c in x+y}.keys()

適切なハッシュ関数を想定してO(N)にする必要があります。

Wasiの8文字のset実装は、ルールに違反していると思わない場合に優れています。


いいね!Pythonの理解力は非常にエレガントで強力です。
OI

3

PHP、42分の69の 41分の68文字

関数宣言を含めると68文字です。

function m($a,$b){return array_keys(array_flip($a)+array_flip($b));}

関数宣言を含まないのは41文字です。

array_keys(array_flip($a)+array_flip($b))

3

Rubyの1つの方法

上記のルールを守るために、JavaScriptソリューションと同様の戦略を使用し、中間体としてハッシュを使用します。

merged_arr = {}.tap { |hash| (arr1 + arr2).each { |el| hash[el] ||= el } }.keys

基本的に、これらは上記の行で行っている手順です。

  1. merged_arr結果を含む変数を定義します
  2. 一意の要素を配置するための仲介として空の名前のないハッシュを初期化します
  3. を使用Object#tapしてハッシュを生成し(ブロックhash内で参照tap)、後続のメソッドチェーンのためにそれを返します
  4. 連結arr1し、arr2単一の、未処理の配列に
  5. 各要素のel連結配列で、値を入れelhash[el]のない値があればhash[el]、現在存在しません。ここでのメモ化(hash[el] ||= el)は、要素の一意性を保証するものです。
  6. 現在読み込まれているハッシュのキー(または値は同じであるため)を取得します

これはO(n)時間内に実行されるはずです。不正確な記述をした場合、または効率または読みやすさのいずれかのために上記の答えを改善できるかどうかをお知らせください。

可能な改善

ハッシュのキーが一意になり、値が無関係であるため、メモ化の使用はおそらく不要です。したがって、これで十分です。

merged_arr = {}.tap { |hash| (arr1 + arr2).each { |el| hash[el] = 1 } }.keys

私は本当に大好きObject#tapですが、以下を使用して同じ結果を達成できますEnumerable#reduce

merged_arr = (arr1 + arr2).reduce({}) { |arr, val| arr[val] = 1; arr }.keys

あなたも使用することができますEnumberable#map

merged_arr = Hash[(arr1 + arr2).map { |val| [val, 1] }].keys

実際にどうするか

私は二つの配列をマージするように求めていた場合は、すべてのことを言ってarr1し、arr2その結果がこのようなことをmerged_arrユニークな要素を持っており、私の処分で任意のRubyのメソッドを使用することができ、私は単純にこの正確な問題を解決するために意図されたセットunion演算子を使用します。

merged_arr = arr1 | arr2

Array#|ただし、のソースを簡単に確認すると、ハッシュを中間体として使用することが、2つの配列間で一意のマージを実行するための許容可能な解決策であると思われることが確認できます。


「一意性を検出するために標準ライブラリの配列関数を使用しないでください(ただし、標準ライブラリを構成する他のものは大丈夫です)」
John Dvorak 14年

2番目の例では、どのようにその規則に違反していますか?メモ化はハッシュで実行されています。それも許可されていませんか?
OI 14年

2
Array.prototype.unique = function()
{
  var o = {},i = this.length
  while(i--)o[this[i]]=true
  return Object.keys(o)
}

n個の配列を取る関数は次のようになります。

function m()
{
  var o={},a=arguments,c=a.length,i;
  while(c--){i=a[c].length;while(i--)o[a[c][i]] = true} 
  return Object.keys(o);
}

ゴルフ、これはうまくいくと思います(117文字)

function m(){var o={},a=arguments,c=a.length,i;while(c--){i=a[c].length;while(i--)o[a[c][i]]=1}return Object.keys(o)}

更新 元のタイプを保持したい場合は、

function m()
{
  var o={},a=arguments,c=a.length,f=[],g=[];
  while(c--)g.concat(a[c])
  c = g.length      
  while(c--){if(!o[g[c]]){o[g[c]]=1;f.push(g[c])}}
  return f
}

またはゴルフ149:

function m(){var o={},a=arguments,c=a.length,f=[],g=[];while(c--)g.concat(a[c]);c= g.length;while(c--){if(!o[g[c]]){o[g[c]]=1;f.push(g[c])}}return f}

とを区別123したい場合、これはまだいくつかの疑問を投げかける可能性'123'があります。


答えてくれてありがとう。それは印象的に短いですが、これは問題の半分しか行いません。また、ソリューションに実際のマージ部分を(元の例と同じであっても)含めて、すべてを1つの関数にまとめる必要があります。また、これに加えて「ゴルフ」バージョンを提供できますO(N)か(そのまま)。
hkk 14年

これにより、すべてのメンバーが文字列にキャストされます。たとえば、次のようにm([1,2,3,4,5],[2,3,4,5,6],[2,3,4,5,6,7])なります["1", "2", "3", "4", "5", "6", "7"]
ジョージリース14年

2

python、46

def A(a,b):print[i for i in b if i not in a]+a

または、単に集合演算を使用する

python、8

set(a+b)

1
残念ながら、明確ではありませんでした。集合演算を使用することも不正行為です。
hkk 14年

aに重複がある場合、またはbに重複があり、その要素がaにない場合、最初のコードは重複します。
ヴェダントカンディ

2

Perl

サブルーチン内のコードブロックのみをカウントする場合、23バイト。グローバル値の上書きが許可されている場合、21になります(myコードから削除されます)。順序は関係ないため、要素はランダムな順序で返されます。複雑さに関しては、平均してO(N)です(ハッシュ衝突の数に依存しますが、まれにあります-最悪の場合はO(N 2)になります(しかし、これは起こりません、Perlは病理学的ハッシュを検出できるからです)、およびそのような動作を検出したときにハッシュ関数のシードを変更します)。

use 5.010;
sub unique{
    my%a=map{$_,1}@_;keys%a
}
my @a1 = (1, 2, 3, 4);
my @a2 = (3, 4, 5, 6);
say join " ", unique @a1, @a2;

出力(ランダム性も示す):

/tmp $ perl unique.pl 
2 3 4 6 1 5
/tmp $ perl unique.pl 
5 4 6 2 1 3

2

Fortran:282 252 233 213

ゴルフバージョン:

function f(a,b,m,n) result(d);integer::m,n,a(m),b(n),c(m+n);integer,allocatable::d(:);j=m+1;c(1:m)=a(1:m);do i=1,n;if(.not.any(b(i)==c(1:m)))then;c(j)=b(i);j=j+1;endif;enddo;allocate(d(j-1));d=c(1:j-1);endfunction

これは無限に良く見えるだけでなく、人間が読める形式で実際にコンパイルされます(ゴルフ形式では長すぎます):

function f(a,b,m,n) result(d)
  integer::m,n,a(m),b(n),c(m+n)
  integer,allocatable::d(:)
  j=m+1;c(1:m)=a(1:m)
  do i=1,n
     if(.not.any(b(i)==c(1:m)))then
        c(j)=b(i);j=j+1
     endif
  enddo
  allocate(d(j-1))
  d=c(1:j-1)
end function

これはO(n)、にコピーaしてcから、bすべてをチェックする必要がありcます。最後の手順は、c初期化されていないために含まれるガベージを削除することです。


2

Mathematica 10文字

Union[a,b]

例:

a={1,2,3,4,5};
b={1,2,3,4,5,6};
Union[a,b]

{1、2、3、4、5、6}

Mathematica2 43文字

Sort@Join[a, b] //. {a___, b_, b_, c___} :> {a, b, c}

8
これは、標準のライブラリ配列メソッドを使用するというカテゴリーに入ると思います。
hkk 14年

こんにちは@ cloudcoder2000。MathematicaでUnionを使用するために特定のライブラリを呼び出す必要はありません。
ムルタ14年

5
私の意見では、組み込み関数を使用して、質問がしていることを正確に行うことは不正行為です。
コンラッドボロウスキ14年

ok ok .. 2番目のコードはUnionを使用しません。
ムルタ14年

1
Tally[Join[a, b]][[;; , 1]]また、不正行為になると思います;-)ところで、1文字の変数を使用して文字を保存できます。
イヴクレット14年

1

Javascript 86

ゴルフバージョン:

function m(a,b){var h={};return a.concat(b).filter(function(v){return h[v]?0:h[v]=1})}

読み取り可能なバージョン:

function merge(a, b) {
  var hash = {};
  return a.concat(b).filter(function (val) {
    return hash[val] ? 0 : hash[val] = 1;
  });
}

1
これはfalsey値を無視します...をm([1,0,0,0,0],[0,1,0])返します[1]
ジョージリース14年

1
に変更h[v]=vh[v]=1ます。
ジョージリース14年

@GeorgeReithをよく見つけました!86から84に行った:)
バートランド14年

まだ86ですが、ゴルフのバージョンではなく、読み取り可能なバージョンから2つのキャラクターを削除したため、混乱したと思います。
ジョージリース14年


0

フレームワークの背後にある基礎となるオブジェクトを効率的に使用するJavaScriptベースの実装を探している場合は、Setを使用します。通常、実装では、Setオブジェクトは挿入時に何らかのバイナリ検索インデックスを使用して固有のオブジェクトを本質的に処理します。Javaではlog(n)、1つのセットに複数のオブジェクトを含めることができないという事実に基づいたバイナリ検索を使用した検索であることを知っています。


これがJavascriptにも当てはまるかどうかはわかりませんが、n*log(n)実装には次のスニペットのような単純なもので十分かもしれません。

JavaScript、61バイト

var s = new Set(a);      // Complexity O(a.length)
b.forEach(function(e) {  // Complexity O(b.length) * O(s.add())
  s.add(e);
}); 

オンラインでお試しください!


上記のスニペットがを使用しa = [1,2,3]b = [1,2,3,4,5,6]次にを使用する場合s=[1,2,3,4,5,6]

あなたがの複雑さを知っている場合はSet.add(Object)JavaScriptで関数が私に知らせて、これの複雑さがあるn + n * f(O)ところf(O)の複雑さですs.add(O)


0

APL(Dyalog Unicode)、O(N)、28バイト

匿名の暗黙の挿入機能。

(⊢(/⍨)⍳∘≢=⍳⍨),

オンラインでお試しください!

, 引数を連結します。オン)

() 次の匿名暗黙関数を適用します。O(1)

   ⍳⍨ インデックスselfie(配列全体の各要素の最初の出現のインデックス); オン)

  = 要素ごとに要素を比較します。オン):

   ⍳∘≢ 配列の長さのインデックス。オン)

(/⍨) それを使用してフィルタリングします。オン):

   変更されていない引数。O(1)

O(N + 1 + N + N + N + N + 1)= O(N)


-2

JavaScript、131文字

var array1 = ["Vijendra","Singh"];   
var array2 = ["Singh", "Shakya"];     
result = Array.from(new Set([...array1, ...array2]))

4
PPCGへようこそ!これがどの言語であるかを教えて、読みやすくするためにコードとしてフォーマットしてください。(これは、コード行を4つのスペースでインデントすることで機能します)。また、あなたのアプローチの説明をいただければ幸いです。
ライコニ

それは単なるJavaScriptコードです。
-deepak_pal

@techdeepakこのような重要な情報を投稿に追加し、適切にフォーマットし、構文の強調表示を追加し、アルゴリズムの複雑さを最速アルゴリズムで記述することができます。これは最速のアルゴリズムです。現状では、この投稿はかなり低品質です。
ジョナサンフレッチ

-2

PHPの約28文字[配列変数と結果変数の例は省略]。

$ array1 = array(1、2、3); $ array2 = array(3、4、5);

$ result = array_merge($ array1、$ array2);


質問から:標準ライブラリの配列関数を使用して、一意性を検出したり、セット/配列をマージしたりしないでください。さらに、これは実際には配列から重複を削除しません
ジョーキング

質問からこの重要な行を見落としていると思います:「一意性の検出またはセット/配列のマージに標準ライブラリの配列関数を使用しないでください
ピーターテイラー

はい。そのとおりです。それを指摘してくれてありがとう。批評は謙虚に受け入れられました。
エンドリ

@ジョーキング。「標準ライブラリを使用しないでください...」について、あなたは絶対に正しいです。残りは間違っています。重複は削除されません。php.net/manual/en/function.array-merge.php。PHPのドキュメントをすべて読むことをお勧めします。私はそれが仕事をすることを100%確信しています。どの配列を重複と見なすかに注意する必要があります。乾杯。
エンドリ

1
私は文字通りあなたの提出物コードを変更なしで実行し、出力には重複があります。ドキュメントを読む必要があるように見えます。ただし、配列に数値キーが含まれている場合、後の値は元の値を上書きしませんが、追加されます
Jo King
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.