web-dev-qa-db-ja.com

配列内で2回発生しない唯一の数を見つける方法

以下は、就職の面接からのものです。

整数を含む特定の配列では、繰り返されない1つを除き、各数値は1回繰り返されます。繰り返さない番号を見つける関数を作成します。

HashSetの使用を考えましたが、すべてが複雑になる可能性があります...

シンプルなソリューションのアイデアはありますか?

56
Adam

0に初期化された整数の「結果」を定義し、その後、配列内のすべての要素にXORロジックを適用することにより、ビットごとの演算を行います。

最後に、「結果」は、一度だけ表示される唯一の要素に等しくなります。

result = 0
for i in array:
  result ^= i
return result

http://en.wikipedia.org/wiki/Bitwise_operation#XOR

たとえば、配列に要素[3、4、5、3、4]が含まれている場合、アルゴリズムは

3 ^ 4 ^ 5 ^ 3 ^ 4

ただし、XOR演算子^は結合的で可換であるため、結果も次のようになります。

(3 ^ 3)^(4 ^ 4)^ 5

任意の整数iに対してi ^ i = 0であり、i ^ 0 = iであるため、

(3 ^ 3)^(4 ^ 4)^ 5 = 0 ^ 0 ^ 5 = 5

134
dhokas

この質問を見たことがあります。それはトリックです。繰り返されるすべての数字が正確に2回現れると仮定すると、これを行います。

int result = 0;
for (int a : arr)
    result ^= a;
17
Paul Boddington

さらに別の「通常の」ソリューション(Java):

public static int findSingle(int[] array) {

    Set<Integer> set = new HashSet<Integer>();

    for (int item : array) {
        if (!set.remove(item)) {
            set.add(item);
        }
    }       

    assert set.size() == 1;

    return set.iterator().next();
}

私の意見では、XORを使用したソリューションは、とても美しいです。

これはXORほど高速ではありませんが、HashSetを使用するとO(n)に近くなります。確かに読みやすくなります。

14
user3078523

最良の答えは既に与えられています(要素のXOR)。これは、より一般的な代替方法を提供することです。

入力配列がソートされる場合(ソートすることができます)、単純にペアで要素を反復処理(2ずつステップ)でき、「ペア」の要素が異なる場合は完了です。

_public static int findSingle(int[] arr) {
    Arrays.sort(arr);
    for (int i = 0, max = arr.length - 1; i < max; i += 2)
        if (arr[i] != arr[i + 1])
            return arr[i];
    return arr[arr.length - 1]; // Single element is the last
}
_

注:このソリューションは、入力配列をソートします。これが望ましくないか許可されていない場合は、最初にクローンを作成できます。

_arr = arr.clone();
_

入力配列がソートされている場合、Arrays.sort(arr)呼び出しはもちろん省略できます。

一般化

このソリューションの利点は、比較可能なすべてのタイプに適用できるため、ソートできる( Comparable を実装するタイプ)、たとえば String または DateXORソリューションは数値のみに制限されています。

これは、比較可能な任意の要素タイプの入力配列を受け取るわずかに変更されたバージョンです。

_public static <E extends Comparable<E>> E findSingle(E[] arr) {
    Arrays.sort(arr);
    for (int i = 0, max = arr.length - 1; i < max; i += 2)
        if (arr[i].compareTo(arr[i + 1]) != 0)
            return arr[i];
    return arr[arr.length - 1]; // Single element is the last
}
_

注:ほとんどの場合、 arr[i].equals(arr[i + 1]) を使用する代わりに、Comparable.compareTo()を使用して要素を比較することもできます。詳細については、リンクされたjavadocを参照してください。関連部分の引用:

強くお勧めしますが、notは_(x.compareTo(y)==0) == (x.equals(y))_を厳密に要求します。一般的に、Comparableインターフェイスを実装し、この条件に違反するクラスは、この事実を明確に示す必要があります。推奨される言語は、「注:このクラスには、等しいと一致しない自然な順序があります。」

これで、たとえば_String[]_でこれを呼び出すことができます。

_System.out.println(findSingle(new String[] { "1", "2", "3", "1", "3" }));
_

出力:

_2
_

最終ノート:

問題ステートメントから開始して、要素のオカレンスが3つ以上あるかどうかはチェックされません。また、配列の長さが奇数であるかどうかもチェックされません。また、2番目の例ではnull値をチェックしません。これらは必要に応じて追加されます。

5
icza

以下に、それをやや難読化した方法を示します。

List list = Arrays.asList(a);
int result;
for(int i:a)
{
    if(list.indexOf(i)==list.lastIndexOf(i))
    {
        result = i;
        break;
    }
}

resultには、繰り返されない値が含まれます。

2
KSFT