2つの配列が異なる1つの要素。効率的に見つける方法は?


22

私はコーディング面接の準備をしていますが、この問題を解決する最も効率的な方法を実際に見つけることはできません。

ソートされていない数値で構成される2つの配列があるとします。配列2には、配列1にはない数値が含まれています。両方の配列はランダムに配置された番号を持ち、必ずしも同じ順序または同じインデックスではありません。例えば:

配列1 [78,11、143、84、77、1、26、35 .... n]

配列2 [11,84、35、25、77、78、26、143 ... 21 ... n + 1]

異なる数を見つけるための最速のアルゴリズムは何ですか?その実行時間は何ですか?この例では、探している数字は21です。

私のアイデアは、配列1を実行し、配列2からその値を削除することでした。完了するまで繰り返します。これは、実行時間に近いはずです。O(nlogn)


@Jandvorak回答ありがとうございます。私は遅れて起きて、これを投稿した後にたまたま眠りに落ちました。配列はソートされておらず、すべてのアイテムは両方の配列のランダムなインデックスで表示されます。
コンスタンティノスパラキス

@KonstantinoSparakis:この説明は、両方の配列に同じ位置の要素が含まれていると仮定する回答を無効にします。
マリオサーベラ


@Paparazziは、メタソフトウェアエンジニアリングで読んだ解決策を探していただけでしたが、解決策を得るためにどこへ行くべきかを知りましたが、当時はCSフォーラムについては知りませんでした。MODをクリーンアップするために通知しました。
コンスタンティーノスパラキス16

@Paparazziはそれを裏付けるメタ投稿がありますか?私は個人的には、このポリシーをうまく実装する方法がわかりません。
djechlin

回答:


30

この問題を解決するには、実行時間が異なる4つの主な方法があります。

  • ソリューション:これが提案するソリューションになります。配列はソートされていないため、削除には直線的な時間がかかることに注意してください。n個の削除を実行します。したがって、このアルゴリズムには2次時間がかかります。O(n2)n

  • 解決策:事前に配列をソートします。次に、線形検索を実行して、個別の要素を識別します。このソリューションでは、実行時間はソート操作に支配されるため、 O nO(nlogn)上限。O(nlogn)

問題の解決策を特定するときは、常に自問する必要があります。この場合、データ構造を巧みに使用することができます。必要なのは、1つの配列を反復処理し、他の配列で繰り返し検索を実行することだけです。(予想される)一定時間内にルックアップを実行できるデータ構造は何ですか?ご想像のとおり、ハッシュテーブルです。

  • 解決策(予想):最初の配列を反復処理し、要素をハッシュテーブルに格納します。次に、2番目の配列で線形スキャンを実行し、ハッシュテーブルの各要素を検索します。ハッシュテーブルで見つからない要素を返します。この線形時間ソリューションは、ハッシュ関数に渡すことができるあらゆるタイプの要素に対して機能します(たとえば、文字列の配列に対しても同様に機能します)。O(n)

上限保証が必要で、配列が厳密に整数で構成されている場合、おそらく、Tobi Alafinによって提案されたものが最適なソリューションです(このソリューションでは、2番目の配列で異なる要素のインデックスは提供されません) :

  • 解(保証):最初の配列の要素を合計します。次に、2番目の配列の要素を合計します。最後に、減算を実行します。この解決策は、ビット単位のXOR演算子のおかげで、値が固定長のビット文字列として表現できる任意のデータ型に実際に一般化できることに注意してください。これは、Ilmari Karonenの回答で徹底的に説明されています。 O(n)

最後に、(整数配列の同じ仮定の下で)別の可能性は、カウントソートなどの線形時間ソートアルゴリズムを使用することです。これにより、O nからのソートベースのソリューションの実行時間が短縮されます。から O n )までO(nlogn)O(n)


4
ただし、数値が十分に大きくなると、加算は線形になりません。
セージボルシュ

9
加算アルゴリズムの良い点の1つは、整数だけでなく、任意のアーベル群で動作することuint64です(最も注目すべきは、cc @sarge)。
ジョンドヴォルザーク

6
@Abdulは、整数が非常に大きい場合、追加するためにをとるふりをすることはできません。あなたがそれを説明するならば、私は複雑さがO n ln n )に成長すると信じています。ただし、通常の加算の代わりにXORを使用すると、入力で任意の数を許可しながら、それを解決できます。O(n)O(nlnn)
ジョンドヴォルザーク

2
@JanDvorakいいえ、ありません。アーベル群で定義された操作には一定の時間がかかると仮定しています。それは単に想定することはできません。
UTF-8

2
@ UTF-8私はそれを想定していません。しかし、これは有限グループ(uint64)で行われ、インプレースの桁ごとの加算(加算)は、アウトオブプレースオペランドのサイズが線形です。だから、このような基に和を計算することであるオペランドの合計サイズで線形時間。Znd
ジョンドヴォルザーク

16

の溶液により提案された差分オブ和トビマリオは、実際には、我々は(一定時間)二項演算を定義することができるため、他のデータタイプに一般化することができるあります。Θ(n)

  • 合計、そのような任意の値のこととB⊕のBが定義されている同じタイプの(または操作者がいることのいくつかの適切なスーパータイプ、の少なくともまだ定義されています)。abab
  • 連想、その結果B C = B Ca(bc)=(ab)c
  • 可換ように、B = B 。そしてab=ba
  • cancellative逆オペレータが存在するように、満足することB B =。技術的には、n要素の2つの合計をそれぞれ「減算」する時間がOn 時間を超えない限り、この逆演算は必ずしも一定時間である必要はありません。abb=anOn

(型が有限数の異なる値しか受け取れない場合、これらのプロパティはそれをアーベル群にするのに十分です;そうでなくても、少なくとも 換相殺半群になります。)

このような操作を使用して、我々は、アレイの"和"を定義することができ、A = 12... Nとしてのa=a1a2an 別のアレイの所与 B = B 1B 2... B NB N + 1のすべての要素を含むプラス1つの追加の要素 Xを、我々はこのように持っている

a=a1a2an
b=b1b2bnbn+1aバツ、そして我々は計算することによって、この余分な要素を見つけることができるように: X = b=aバツ
バツ=ba

アレイの値が整数である場合、例えば、次に加算(または整数モジュラ加算、有限長の整数タイプの)オペレータとして使用することができる逆の動作と減算と、。代替的に、のために任意の値を持つ固定長のビット列として表すことができるデータの種類、我々が使用することができ、ビット単位XORを両方として

より一般的には、末尾のパディングを可逆的に削除する方法がある限り、必要に応じて同じ長さまでパディングすることで、可変長の文字列にビット単位のXORメソッドを適用することもできます。

場合によっては、これは簡単です。たとえば、Cスタイルのnullで終了するバイト文字列は暗黙的に独自の長さをエンコードするため、このメソッドをそれらに適用するのは簡単です。最終結果。ただし、中間のXOR-sum文字列にはnullバイト含めることができるため、その長さを明示的に格納する必要があります(ただし、必要なのは1つまたは2つだけです)。

1001232バイト長の場合、各文字列の長さを32ビット整数としてエンコードし、文字列の先頭に追加できます。または、プレフィックスコードを使用して任意の文字列の長さをエンコードし、それらを文字列の先頭に追加することもできます。他の可能なエンコーディングも存在します。

Θn

唯一の潜在的にトリッキーな部分は、キャンセルを機能させるために、各値に一意の標準ビット文字列表現を選択する必要があることです。2つの配列の入力値が与えられる場合、困難です異なる同等の表現で。ただし、これはこの方法の特定の弱点ではありません。入力に同値性が決定できない値が含まれることが許可されている場合、この問題を解決する他の方法も失敗させることができます。


これはとても興味深いことです。ありがとう@IlmariKaronen
Konstantino Sparakis

14

これをトビの答えに対するコメントとして投稿したいのですが、まだ評判がありません。

各リストの合計を計算する代わりに(特に大きなリストであるか、合計するとデータ型がオーバーフローする可能性のある非常に大きな数が含まれる場合)、代わりにxorを使用できます。

各リストのxor-sum(x [0] ^ x [1] ^ x [2] ... x [n])を計算してから、これら2つの値をxorします。これにより、無関係なアイテムの値が得られます(ただし、インデックスは得られません)。

これはまだO(n)であり、オーバーフローの問題を回避します。


3
また、XORを使用します。少し見栄えが良いように見えますが、公平に言えば、これを実装する言語がラッピングによるオーバーフローをサポートしている限り、オーバーフローは実際には問題ではありません。
マーティンエンダー

14

要素= Sum(Array2)-Sum(Array1)

私はこれが最も最適なアルゴリズムだと心から疑います。しかし、それは問題を解決する別の方法であり、それを解決する最も簡単な方法です。それが役に立てば幸い。

追加された要素の数が複数の場合、これは機能しません。

私の答えは、最高の場合、最悪の場合、および平均的な場合の実行時の複雑さは同じですが、

編集
いくつかの思考の後、私の答えはあなたの解決策だと思います。

nn11=n12=n+11=n

2n121=1

2n1+1=2n

Θn

編集:
データ型に関するいくつかの問題によりreffu示唆する XOR合計 はより適切です。


数値を合計すると丸め誤差が発生する可能性があるため、値が浮動小数点数の場合、この方法では正確な答えが得られない可能性があることに注意してください。ただし、a)整数型でオーバーフロー時のラップアラウンド動作が明確に定義されているか、b)オーバーフローしないように十分に広い型の変数に合計を格納する場合、整数値に対して機能します。
イルマリカロネン

Rubyの「BigNum」クラスはおそらくこれを処理できます。
トビアラフィン

配列に文字列などが含まれている場合や、意味のある追加ができない場合はまったく機能しません。
gnasher729

うん、わかった。「XOR」の使用はどうですか?フロートでも動作しますか?
トビアラフィン

はい。また、ポインタ、および一般に固定数のビットで構成されるすべてのもの。多くの言語はそれをサポートしていませんが、それは根本的な問題ではありません。モジュラー加算/減算も同じケースで機能します。
ハロルド

1

配列1を取得して要素をランダムな位置に挿入することによって配列2が作成された、または配列2を取得してランダムな要素を削除して配列1を作成したと仮定します。

すべての配列要素が明確であることが保証されている場合、時間はO(ln n)です。場所n / 2の要素を比較します。それらが等しい場合、余分な要素はn / 2 + 1から配列の最後までです。それ以外の場合、0からn / 2までです。等々。

配列要素が明確であることが保証されていない場合:配列1の数値1のn倍、配列2の任意の位置に数値2を挿入できます。その場合、数値2がどこを見てもわからない配列要素。したがって、O(n)。

PS。要件が変更されたため、利用可能なライブラリについてライブラリを確認してください。あることのMacOS / iOSでは、あなたは請求に頼ることなく、アレイ1からのすべての数字を削除し、アレイ2からのすべての数字を追加し、どのような残っていることは、アレイ2にあるすべてのものではなく、配列1に、NSCountedSetを作成する1追加項目。


この答えは正しかったのですが、質問はあなたの仮定を無効にする新しい要件で編集されました。
マリオサーベラ

新しい答えは正しいようです。時間の複雑さは何ですか。
トビアラフィン

さて、最初にコードを書くのに必要な時間は何ですか。些細なことです。NSCountedSetはハッシュを使用するため、時間の複雑さは「通常は線形」です。
gnasher729 16

-1

var最短、最長。

最短の参照をマップに変換し、現在の値がマップに表示されなくなるまで最長のループを実行します。

javascriptのこのようなもの:

if(arr1.length> arr2.length){最短= arr2; 最長= arr1; } else {shortest = arr1; 最長= arr2; }

var map = shortest.reduce(function(obj、value){obj [value] = true; return obj;}、{});

var difference = longest.find(function(value){return !!! map [value];});


説明のないコードは、ここでは適切な答えとしてカウントされません。また、なぜ使用しますか!!! ?
邪悪な

-1

時間の複雑さにおけるO(N)ソリューションスペースの複雑さに関するO(1)

問題文:array2には、array1のすべての要素と、array1に存在しない他の要素が1つ含まれていると仮定します。

解決策は次のとおりです。xorを使用してarray1に存在しない要素を見つけます。手順は次のとおりです。1. array1から開始し、すべての要素のxorを実行して変数に格納します。2. array2を取得し、array1のxorを格納する変数を使用してすべての要素のxorを実行します。3.操作を実行した後、変数にはarray2にのみ存在する要素が含まれます。上記のアルゴリズムは、xor "a xor a = 0" "a xor 0 = a"の次のプロパティのために機能します。これで問題が解決することを願っています。また、上記の解決策も問題ありません

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.