データ構造シリーズ:ハッシュ表
導入
フォークを使ってパスタを食べたり、スプーンにスープを食べたり、箸を食べたりします.各々のsilverwaresはその利点/欠点を持っています、したがって、それがそれがよく相互作用する食物のためにもう一方よりよく働きます.そのように、別のデータ構造は、より適しており、状況/ユースケースに基づいて他よりも良い実行します.彼らはそれぞれ賛否両論を持っている.それはあなたが状況/目標に基づいて適切なデータ構造を選択できるようになるように、これらの長所と短所を理解することは、より良いプログラマに役立つことができます、それは大幅に適用されているアルゴリズムのパフォーマンスを向上させるのに役立ちます.私はJavaScriptでよく知られているプログラミングデータ構造にこれらのブログシリーズを入れていて、将来の1つのブログ柱でそれらをすべてリンクします.あなたが質問をするならば、コメントを残してください!
目次
1 .What Is Hash Table?
2 .Hash Function
3 .Implementation in JavaScript
4 .Helper Functions
5 .Handling Collisions
6 .Big O
7.Helpful Resources
1ハッシュテーブルとは
Hash Table is a data structure of associative array that stores key/value paired data into buckets.
Considered being one of the most important data structures in computing, Hash Table is used in many areas of applications: password verifications, cryptography, compilers, and the list goes on. Due to its efficiency and speed in searching, insertion, and removal of data, it is a widely applicable and preferred data structure in many cases. A Hash Table is a data structure of associative array that stores data as a key/value pair into a bucket.
ハッシュテーブルがどのように働くかは、それが入力としてキーと値をとるということです、そして、インデックスにそれを回すハッシュ関数でキーを実行します.このプロセスをハッシングと呼ぶ.インデックスは、テーブルのバケツに入力の値をマップするために使用されます.ハッシュ関数は不可逆です.そして、それはそれを安全で信頼できるようにします.しかし、2つの異なるキーが同じインデックスになる可能性があり、これは衝突と呼ばれます.衝突した場合、前のキーのプレースホルダをオーバーライドできます.ハッシュ衝突を扱う様々な方法があります--別々の連鎖は、しばしば同じインデックスに複数のデータを保存するためにバケツの中でリンクリストを使用するそれらのうちの1つである.あとでこのポストに入ります.しかし最初に、ハッシュ関数がどのように動作するかを議論しましょう.
2 .ハッシュ関数
The hashing process in computing is like hashing a potato to make hashed brown.
ハッシュ関数、ハッシュアルゴリズムは与えられた入力から固定長の結果を生成します.このプロセスをハッシングと呼ぶ.固定長の結果はハッシュバケットに入力をマップするインデックスとしてハッシュテーブルで使用されます.コンピューティングのハッチングプロセスは、ハッチングブラウンを作るためにジャガイモをハックするようです.あなたは、ハッシュ関数の結果としてハッシュされたインデックスとして、ジャガイモのキー入力、ハッシュ関数としてgrater、および破砕ジャガイモと考えることができました.どのようにジャガイモ全体に刻まれたジャガイモを返すことができないように、ハッシュ関数は不可逆です-それは1つの方法のアルゴリズムです.
以下はJavaScriptのハッシュ関数の例です.
function hash (key, size) {
let hashedKey = 0;
for (let i = 0; i < key.length; i++) {
hashedKey += key.charCodeAt(i)
}
return hashedKey % size
}
擬似コード:key
ハッシュするsize
ハッシュバケットのhashedKey
終了時に0を返すhashedKey / size
そしてそれを新しいhashedKey
hashedKey
上記のアルゴリズムでは、変数
hashedKey
AS0
. この変数の値は文字列に基づいて変更され、この関数の結果として返されます.各文字を数字に表現する方法が必要です.このようにして、関数を通過するマッチング文字列キーは常に同じ整数に変換されます.JavaScriptの文字列メソッドcharCodeAt()
文字列文字をUTF - 16コード単位を表す整数に変換できます.これにより、forループはキー入力のすべての文字を反復します.反復される各文字に対して、
charCodeAt()
文字を変換し、hashedKey
変数を先頭に定義します.各文字を表す整数を合計すると、モジュロ演算を実行します%
使用size
バケット(関数の2番目の引数)の除数として.モジュロ演算は、結果の整数が0からバケツのサイズまでの範囲にあることを保証するだけでなく、結果を不可逆にします.これは、より良く改善される非常にシンプルで基本的なハッシュ関数です.チェックアウトするthis blog post あなたは数学者や世界中のコンピュータ科学者によって設計されたさまざまなハッシュ関数について学ぶことに興味がある場合.今、JavaScriptのハッシュテーブルを実装する時間です!
JavaScriptの実装
class HashTable {
constructor(size=53) {
this.size = size
this.buckets = new Array(size);
}
_hash (key) {
let hashedKey = 0;
for (let i = 0; i < key.length; i++) {
hashedKey += key.charCodeAt(i)
}
return hashedKey % this.size
}
}
let table = new HashTable()
console.log(table) // HashTable {size: 53, buckets: Array(53)}
The above Hash Table class has two properties:
-
size
: the number representing thesize
of the buckets, and we are using prime number 53 as a default value (choosing a prime number for the hash table's size reduces the chances of collisions) -
buckets
:buckets
are the placeholders for each data (key/value pair), and we are usingArray
class to create an empty array with size of 53 indices
And we have the _hash
method similar to what we created earlier, but only difference is that it is not taking in the size
as second argument since we are using the size
of the object created from the Hash Table
class. With this, we can create an object with buckets array that contains default size
of 53 indices or a specified size
.
Let's go ahead and add some methods to this Hash Table!
ヘルパー関数
set()
// adds key-value pair into hash table's bucket
set(key, value) {
let index = this._hash(key)
this.buckets[index] = [key, value];
}
Pseudocode:
- Accepts a
key
and avalue
- Hashes the
key
- Stores the key-value pair in the hash bucket
get()
// retrieves the value of the key from its respective bucket
get(key) {
let index = this._hash(key)
return this.buckets[index][1] // returns value of the key
}
Pseudocode:
- Accepts a
key
- Hashes the
key
- Retrieves the key-value pair in the hash bucket
remove()
// removes the key-value pair from the hash table's bucket
remove(key) {
let index = this._hash(key)
let deleted = this.buckets[index]
delete this.buckets[index]
return deleted
}
Pseudocode:
- Accepts a
key
- Hashes the
key
- Retrieves the key-value pair in the hash bucket and stores it
- Delete the key-value pair in the hash bucket (use
delete
operator to empty the element, doesn't affect the array size) - Returns the stored key-value pair
All the helper functions in this data structure is fairly simple -- they all utilize the hash function we defined earlier to retrieve the index
that is associated with the key
passed, and access the array's element in that index
. There's a problem with these methods though. What happens if the hash function returns the same index for two different inputs? Our hash function is fairly simple so this will happen for sure. If so, it will override the bucket that is already occupied or get
method will retrieve a wrong value that we aren't looking for. How can we improve these helper methods to handle the collisions?
衝突の取り扱い
我々が以前に議論したように、ハッシュ関数が衝突を生じるのは、可能です.残念ながら、状況の中でさえ、衝突はほとんど避けられない.出力より多くの入力によるどんなハッシュ関数も、そのような衝突を必然的に持ちます;見つけるのが難しいほど、ハッシュ関数はより安全です.
衝突を扱うために複数の方法があります、そして、2つの一般のテクニックは別々の連鎖と線形探査です.
別々の連鎖:配列のインデックスにポインティングしているハッシュコードだけがあるならば、値は直接そのインデックスに格納されます.2番目の値のハッシュコードも同じインデックスを指している場合は、インデックス値をリンクリストまたは配列と置換し、そのインデックスを指すすべての値をリストに格納します.値を取得しながら同じロジックを適用すると、バケツが複数のキー値のペアを格納する場合、バケツ内のすべての要素を反復処理する必要があります.要するに、別々の連鎖は衝突で複数のデータを保存するためにバケツの中のオブジェクトのようなリストを作成します.
線形プロービング技術は、空のバケツを見つけるまで、ハッシュされたインデックスをインクリメントし続ける維持の概念に取り組みます.このように、線形プロービングは別々の連鎖より少ないスペースを取って、別々の連鎖よりかなり速く実行します(バケットの中でリストを通してループする必要がないので).
別々の連鎖は、線形プロービングよりもかなり効率的ではありませんが、実装が簡単です.ここでは、別々の連鎖を利用して定義されているヘルパーメソッドを改善する方法を説明します.
set ()
// adds key-value pair into hash table's bucket
set(key, value) {
let index = this._hash(key)
if(!this.buckets[index]) {
this.buckets[index] = [];
}
this.buckets[index].push([key, value]);
}
擬似コード:key
とvalue
key
// retrieves the value of the key from its respective bucket
get(key) {
let index = this._hash(key)
if(this.buckets[index]) {
for(let i = 0; i < this.buckets[index].length; i++) {
if(this.buckets[index][i][0] === key) {
return this.buckets[index][i][1]
}
}
}
return undefined
}
擬似コード:key
key
key
ペアにマッチし、value
一対のundefined
バケツが空ならば// removes the key-value pair from the hash table's bucket
remove(key) {
let index = this._hash(key)
if(this.buckets[index]) {
for(let i = 0; i < this.buckets[index].length; i++) {
if(this.buckets[index][i][0] === key) {
return this.buckets[index].splice(i, 1)
}
}
}
}
擬似コード:key
key
key
ペアにマッチし、ペアを削除し、それを返すビッグ・オー
空間の複雑さ
設定/取得/削除:
参考になるリソース
Online Course (udemyコース)JavaScriptアルゴリズムとデータ構造MasterClassという名前のこのUDEMYコースをチェック!これは作成され、私はこのブログ記事のデータ構造の実装部分のための彼のコードを参照します.個人的には、私はどこからアルゴリズムやデータ構造、特に非技術的な背景から来て知っていない.このコースは非常によく初心者のためのこれらのトピックの基礎を構築するために構築されます.
Visual Animation ビジュアルゴ
データ構造は、コード/テキストを見るだけで、一部の人々のために理解するのが難しいかもしれません.上記のインストラクターは、Visualgoという名前のウェブサイトを使用して、アニメーションを通してアルゴリズムやデータ構造を視覚的に表現しています.
Data Structure Cheat Sheet (インタビューケーキ)
また、ここでは本当によく整理されたカンニングペーパー/データ構造の可視化です.
YouTube動画
私はこのYouTubeのビデオの向こう側に来ました.
!ハーバードのCS 50コースの一部であり、ハッシュテーブルを説明する素晴らしい仕事をしています.
Reference
この問題について(データ構造シリーズ:ハッシュ表), 我々は、より多くの情報をここで見つけました https://dev.to/chuckchoiboi/data-structure-series-hash-table-5h16テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol