web-dev-qa-db-ja.com

2つの配列で最初の共通要素をどのように見つけますか

2つの配列の最初の共通要素をどのように見つけますか?

私の試み:

同時にそれぞれをループし、現在の値を各リストの個別のセットに追加しました。また、現在の要素がすでに他のリストのセットにあるかどうかを確認します。

これがうまくいくかどうかはわかりませんが、うまくいく場合はなぜですか?私はそれが共通の要素を見つけることを知っていますが、最初の共通の要素を見つけますか?反例は思いつきませんが、間違いなく正しいと自分に納得させるのに苦労しています。また、それが正しい場合でも、最初の共通要素を見つけるより良い方法はありますか?

3
user1136342

配列のソートは機能しません。順序を変更すると、どの要素が最初に一致するかを判別できなくなります。

交差ソリューションも機能しません。これを使用するには、両方の配列をセットクラスにコピーする必要があります。これは、定義上、順序を保持しません。

正解です。ハッシュセットを使用して検索時間を短縮することを検討します。

6
eric w

問題がL2にも含まれているリストL1の最初の要素を見つけることである場合、L1の要素を1つずつ実行し、現在の要素がL2にあるかどうかを確認します。 2番目のテスト(「L2の要素」)は、L2のすべての要素に対する単純なループで実行できます。または、L2のすべての要素を最初にハッシュセットに入れると、プロセスをかなり高速化できます。

これで、L1の一部でもあるL2の最初の要素を探しているときに、L1とL2の役割を切り替えることができることは明らかです。そして、L1またはL2のいずれかで位置インデックスが最も低いリストの1つから共通の要素を探している場合は、これまでに説明した両方の手順を実行し、2つの異なる要素が見つかった場合は、どちらかでインデックスが最も低い要素を選択します。 L1またはL2。

このアプローチには1つの欠点があります。L1とL2が巨大で、各リストの最初にいくつかの非共通要素がある場合は、L1、L2、またはその両方の完全なリストを読む必要があります。 L1とL2の最初の要素。したがって、上記のソリューションのアイデアは、次のサブセットを使用することです。

_K1(n)="the first n elements of L1" (or L1 if n>length(L1))
K2(n)="the first n elements of L2" (or L2 if n>length(L2))
_

nを1からmax(length(L1),length(L2)まで実行し、K1とK2にnごとに共通の要素があるかどうかを確認します。 K1(n)とK2(n)に共通の要素がある最小のnは、L1のn番目の要素またはL2のn番目の要素が探している要素であることを示しています。

K1(n)とK2(n)が互いに素であり、nからn + 1に移動する場合、L1とL2から新しく導入された_n+1_ st要素がK2(n + 1)またはK1(n + 1)。増分的に拡張されるK1およびK2の高速ハッシュセットデータ構造を使用すると、nが大きい場合でもこのプロセスを高速に保つことができます。また、特定のnに共通の要素が見つかった場合は、プロセスをすぐに停止できます。それはあなたがあなたの投稿で説明したものです。

このアプローチは、たとえばL1とL2に100万を超える要素がある場合でもかなり高速に機能しますが、両方のリストの最初の共通要素は、両方のリストの「トップ10」に含まれます。 L1とL2に共通の要素がない場合は、それほど効果はありませんが、他の方法でも同じです。

4
Doc Brown

最初の配列Aの長さがnで、2番目の配列Bの長さがmであるとします。

長さが短い方をループし、その要素をLinkedHashSetに入れます(ソートされているかどうかは関係ありません)。次に、2番目の配列をループして、その要素のセットを照会します。最初の一般的な要素を見つけたら、どこでも休憩してください。

空間の複雑さ:O(m) mがn以下であると仮定すると、時間の複雑さ:O(n)

//Java-like pseudo code
class FirstCommonFinder {
    Element[] firstInput = {….};
    Element[] secondInput = {….};
    Set smallerInput = new LinkedHashSet<>();
    boolean found = false;
    //assuming firstInput is smaller than secondInput
    for (Element input: firstInput) {
        smallerInput.add(input);
    }

    //loop through and break upon first common occurrence 
    for (int i = 0; i < secondInput.length; i++) {
        if (smallerInput.contains(secondInput[i])) {
        found = true;
        break;
    }
}
</ code>

}

両方の入力配列のインデックスを追跡したい場合(一般的に発生する要素の場合)、LinkedHashMapを使用し、配列のインデックスを値として要素キーに配置します。

0
Nabeel

個人的には、setソリューションは機能すると思いますが、O(1)のメモリ使用量でソリューションを最適化したい場合、O(n)になる2つのリストを逆にすることができます。最初から開始し、2つのポインタを使用して、最初の違いを見つけます。違いの直前のノードが答えです。元のリストを変更できない場合は、元に戻すことができます。どちらでもO( n)。

0
Tristan Yan