2011-12-16 8 views
15

intの配列が与えられた場合、各intは 配列に正確にTWICEと表示されます。このintのペアがこの配列の中で最大 の距離を持つようにintを見つけて返します。各要素が2回出現する所与の配列内で最も長い距離の要素を見つけますか?

[2, 1, 1, 3, 2, 3]

2: d = 5-1 = 4; 
1: d = 3-2 = 1; 
3: d = 6-4 = 2; 
return 2 

私のアイデア:

使用ハッシュマップ、キーがa[i]あり、その値はインデックスです。 a[]をスキャンし、各番号をハッシュに入れます。数値が2回ヒットした場合は、そのインデックスから古い数値インデックスを引いた値を使用し、その結果を使用してハッシュの要素値を更新します。

その後、ハッシュをスキャンして最大の要素(距離)のキーを返します。 時間と空間ではO(n)です。

O(n)時間とO(1)時間に行う方法は?

+1

私はあなたが明白にそれをもっと速くすることができると思います...あなたの例では、 'a [0]'の距離が '5 'であることを見いだした後、配列のサイズは '6 'であるからです。 – lapk

+3

@AzzAこれは事実を速めるが、線形漸近成長率には影響しません。 –

+0

これはインタビューの質問ですか? –

答えて

2

あなたは最大距離を持ちたいと思っていますので、検索する数字が最初と最後になる可能性が高いと思います。これは、配列の先頭から末尾まで同時にループする理由です。

[2, 1, 1, 3, 2, 3] 
Check if 2 == 3? 
Store a map of numbers and position: [2 => 1, 3 => 6] 
Check if 1 or 2 is in [2 => 1, 3 => 6] ? 

私は知っている、それは擬似コードではなく、完全ではなく、ただのアイデアを出すことです。

+0

マップを格納することは、マップのサイズがリスト内の別個の要素の数に依存するため、 'O(1)'スペースを使用していないことを意味します。質問はすでにルックアップテーブルの使用を考慮した。 – birryree

+0

はい、理論的にはO()だけを見てください。しかし、プラクシスでは、これはより速く、より少ないスペースを使用します。彼は_always_全体の配列上にマップを作成します! – PiTheNumber

+0

仮定が多少間違っています:真ん中にあるちょっとした振る舞いを除いて、あなたが「よく振舞われた」対しか持っていないと仮定してください: '[1,2,1,3,2,4,4,5,3,6、 5,6]。ここで '3'は特に終わりではありません。 –

0

iLeftインデックスを最初の要素に設定し、iRightインデックスを2番目の要素に設定します。 左のアイテムのコピーを見つけるか、配列の最後を満たすまで、iRi​​ghtインデックスをインクリメントします。最初のケースで - 距離を覚えています。

iLeftをインクリメントします。新しいiRightから検索を開始する。 iRightの開始値は決して減少しません。 Delphiコード:

iLeft := 0; 
    iRight := 1; 

    while iRight < Len do begin //Len = array size 
    while (iRight < Len) and (A[iRight] <> A[iLeft]) do 
     Inc(iRight); //iRight++ 
    if iRight < Len then begin 
     BestNumber := A[iLeft]; 
     MaxDistance := iRight - iLeft; 
    end; 
    Inc(iLeft); //iLeft++ 
    iRight := iLeft + MaxDistance; 
    end; 
+1

[1、2、1、3、4、5、6、7、7、6、5、4,3]:この場合、iLeft == 0、iRight == 3を見つけたら、 iLeft == 1のペアを探しています。しかし、iRightは決して減少することはないので、iRi​​ght == 2を見つけることはできません。したがって、配列の最後に移動します。または、アルゴリズムを正確に理解できないかもしれません... – liori

+0

@liori 'iRight'はすべてのループの最後にリセットされます(' iRight = iLeft + MaxDistance')。だから「iRight」は減少する。あなたの例では '2'のペアは見つかりませんが、このアルゴリズムは正しい結果を与えることができます。しかし、「iRight」が減少するので、それがO(n)であるかどうかは疑問です。 – fefe

+0

@fefeはい、O(n)は私の間違いです。削除されました。 – MBo

0

このアルゴリズムは、O(何らかの不正行為を伴う)(1)空間で、O(N)時間(平均)は、ソース・アレイは、非constする必要がある、最後にそれを破壊します。また、配列内の可能な値を制限します(各値の3ビットはアルゴリズム用に予約する必要があります)。

答えの半分は既に質問にあります。ハッシュマップを使用します。数値が2回ヒットした場合は、インデックスの差分を使用し、最適な結果を更新し、この数値をハッシュマップから削除して空き領域を確保します。 O(1)スペースにするには、ソース配列を再利用するだけです。配列をハッシュマップのインプレースに変換します。

配列要素をハッシュマップセルに変換する前に、その値と位置を覚えておいてください。この後、安全に上書きされる可能性があります。次に、この値を使用してハッシュマップ内の新しい位置を計算し、上書きします。要素は空のセルが見つかるまでこの方法でシャッフルされます。続行するには、まだ並べ替えられていない要素を選択します。すべてが再順序付けされると、すべてのintペアが確実に2回ヒットします。ここでは空のハッシュマップと更新された最良の結果値があります。

配列要素をハッシュマップセルに変換する際に予約ビットが1つ使用されます。最初はクリアされています。値がハッシュマップセルに並べ替えられると、このビットが設定されます。上書きされた要素にこのビットが設定されていない場合、この要素は次に処理されるために使用されます。このビットが上書きされる要素に設定されている場合は、ここで競合が発生しています(このビットが設定されていない最初の要素を選択して上書きします)。

2つ以上の予約ビットが競合する値を連鎖させるために使用されます。チェーンが開始/終了/継続する位置をエンコードします。

ハッシュマップセルには、3つの予約ビット、元の値インデックス、およびこの要素を一意に識別する情報が含まれている必要があります。これを可能にするために、ハッシュ関数は可逆でなければならず、テーブルの中の位置を考慮して値の一部を復元することができます。最も簡単なケースでは、ハッシュ関数はちょうどceil(log(n))最下位ビットです。表中の値は、3つのフィールドで構成さ:

  • 32 - 3 - (ceil(log(n)))上位ビット元の値から

    • 3予約ビット元の配列

    時間複雑度の要素の位置について

  • ceil(log(n))ビットO(n)は平均してのみである。最悪の場合の複雑さはO(n^2)です。

    このアルゴリズムの他の変形は、配列をハッシュマップに順次変換することです:各ステップで 2^mの最初の要素がハッシュマップに変換されています。 mが低い場合、パフォーマンスを向上させるために、一定のサイズの配列をハッシュマップとインターリーブすることができます。 mが高い場合は、すでに処理されている空間ペアが十分にあり、スペースは必要ありません。

  • +0

    詳細な分析をお寄せいただきありがとうございますが、ハッシュksysを維持する方法はありますか?新しい要素が2回ヒットしたかどうかをチェックするためにハッシュキーを使ってハッシュテーブルを検索する必要があるからです。 – user1002288

    +0

    ハッシュキーの半分がハッシュテーブル(上位ビット)に格納されます。他の半分はハッシュテーブル内の位置から復元されます(ハッシュ関数は可逆であるため)。たとえば、テーブルのサイズは32で、番号33を検索します。下位ビット(1)を取ると、テーブルのインデックスは1になります。このテーブル要素の上位ビットと上位ビットの数(32)。一致するものがなければ、競合する値のチェーンに従ってください。チェーンのどこかに32がある場合、この要素は2回ヒットします。見つからなければ、ハッシュマップに追加します。 –

    +0

    つまり、ハッシュキーの一部は、適切なハッシュマップエントリを見つけるために使用されます(衝突を解決する必要がないため、どこにも格納されません)。ハッシュキーの他の部分は、起こりうる衝突を解決するために使用されます(また、すべてのハッシュマップエントリに格納されます)。 –

    0

    O(n)時間とO(1)時間にこれを行う方法はありません。

    関連する問題