web-dev-qa-db-ja.com

配列のインデックス付けはどのように正確に機能しますか?

インデックスの方が速いことはわかっていますが、なぜの方が速いかわかりません。

配列_int[] a = {2,3,6,7}_があるとします。次に、_a[3]_で要素を見つけようとします。これの速度はO(1)になります。どうして? _3_が2つのボックスの後に配置されていることをどのようにして知るのでしょうか。だから私の質問をより明確にするために、ここに私が期待している配列の構造があります。

_index   values 
 [0] ->  [2]
 [1] ->  [3]
 [2] ->  [6]
 [3] ->  [7]
_

なぜ_a[3]_は直接7に行くのですか?

HashTable

HashTableと同じ混乱:

_ hash   values 
 [7nsh] ->  [2]
 [j2ns] ->  [3]
 [9sjm] ->  [6]
 [an5k] ->  [7]
_

ハッシュ関数getValue(6)から値を検索すると、同じキー_9sjm_が生成されますが、キーが3番目の数値に配置されていることがどのように認識されますか?同じようにO(1)になる可能性はありますか?

2
Asif Mushtaq

ハッシュを忘れて、それはそれよりはるかに簡単です。配列は常に連続した記憶場所を使用してメモリに配置されます。コンパイラは、メモリセルxから開始する配列を認識しています。 a [123]に到達する必要がある場合、xに123を加算し、その番号を使用して、要素に到達するためのメモリをアドレス指定します。

実際には123 * elementSizeですが、要点はわかります。常に、1つの乗算、1つの加算演算、および既知の場所からの要素の1つのフェッチが必要です。

6
Martin Maat

配列とハッシュテーブルはどちらもマップです。マップはキーを値に変換します(または値を格納する場所を提供します)。

配列の場合、キーはゼロから始まるJava(および多くの言語))の整数インデックスです。

配列の要素は格納されているので、要素のストレージをそのインデックスに配置するために、単純なポインター演算を整数キー(別名、インデックス式)で使用できます。 「ルックアップ」は必要ありません。むしろ、目的の要素を算術的に特定します。

実際には、これは要素が連続して格納されることを意味します。ただし、intなどのプリミティブ値タイプの配列とJavaのオブジェクトの配列には違いがあることに注意してください。オブジェクトの配列は、実際にはオブジェクトへの参照の配列であり、それらの参照は連続して格納されます。オブジェクト自体は、割り当てられたとき(割り当てられたとき)に応じてより多く格納されます(概念ヒープ内)。したがって、オブジェクトの配列の要素にアクセスするために、通常のポインタ演算はオブジェクト参照を見つけ、次に、取得された参照を介してもう1つの間接参照がオブジェクト自体にアクセスします。


ハッシュテーブルもマップです。ただし、コンパイル時に任意のキータイプ(および値タイプ)を選択できます(ただし、非常に一般的なオブジェクトタイプを選択できます)。ハッシュテーブルは、キーのハッシュ値を使用して、内部のバケット配列の要素間でキーと値のペアを分散します。ハッシュテーブルはO(1)です。これは、ハッシュテーブルに多くの要素がある場合でも、ルックアップは比較的一定であるためです。アイテムを見つけることができます。キーのハッシュ値を使用して、バケットインデックス。次に、通常の配列インデックス付け(ポインタ演算)が適用されて、完全一致が検索されるバケットが取得されます。

したがって、=(=ルックアップと挿入の両方で)をO(1)として)と記述することは、検索のために単純化しすぎです。バケットインデックス計算での衝突により、検索が必要になります複数のアイテムを同じバケットに保存する必要があることを意味します(それが単純なスロットではなくバケットである理由です)。

ただし、ハッシュテーブルが適切に機能している場合は、1つのバケット内のアイテム数を少数(アプリケーションによっては0から10の間)に保ち、バケット配列を定期的に拡張することでこれを実現しますより多くの要素がハッシュテーブルに挿入されるとき。この定期的な拡張には、特に、ハッシュテーブルが拡張することを決定するトリガーが交差したときに、実際のコストがかかります。それでも、その拡張は多くの挿入にわたって償却されたと見なすことができます。これは、拡張を行わない場合、挿入は比較的自由であるため、O(1)も同様です。

ルックアップは、平均して少数の検索を実行します(展開が適切に機能し、ハッシュ関数も適切に機能します)。この数は、ハッシュテーブル内の合計アイテム数に実際には依存しません。このため、ルックアップは基本的に一定です。

基本的なハッシュテーブルは、値によるキーまたはキーと値のペアの検索(つまり、キーのない値が与えられた場合)に特定の高速化を提供しません。基本的なハッシュテーブルで値を検索すると、O(N)になります。それを加速するには、交換されたキーと値の比較可能な2番目のハッシュテーブルが必要です。


メモリ自体(もちろん、簡略化されています)は1つの大きな配列であり、ポインタは、インデックス変数が配列インデックスを保持するのと同じように、アドレスを保持する変数です。メモリシステムは、特定のアドレスに格納されているメモリへの直接アクセスを可能にします。アレイと同様に、メモリシステムはアドレスを格納せず、データバイトのみを格納します。ただし、アドレスをバイトまたはワードに変換する方法を知っているだけです。

4
Erik Eidt