15-ハッシュテーブル

ハッシュテーブルとも呼ばれるハッシュテーブル(ハッシュテーブル)は、値をアドレスにマップして直接アクセスできるデータ構造です.ハッシュテーブルを介して、データにすばやくすばやくアクセスできます.

ハッシュテーブルの原則

数値 x (キー値、キーとも呼ばれます) があるとします。このキー値 x をハッシュ関数を介して数値 k にマップし、この数値 k をテーブル内の位置にマップすると、次のことができます。このテーブルを介してアクセスまたは保存できます。

このことから、ハッシュ テーブルを作成する場合は、まずハッシュ関数を作成する必要があることがわかります。上の図からわかるように、ハッシュ関数の主な機能はキー値をアドレスに変換することであり、これは変更と変更のプロセスとして理解できます. たとえば、キー値 x の範囲が- と の間に、ハッシュ法が10^{9}使用10^{9}されます。このキー値 x の範囲を 0~ に絞り込むことができます10^{5}次に、特定の数を特定の範囲に制限したい場合、モジュロ演算を自然に考えます。剰余演算は、有限数 x を、得られた剰余よりも小さい数にすることができます。ここでは、範囲を 1000 未満に制限します。しかし、ここで問題があります。キーの値を range- 10^{9}toに制限すると10^{9}、ほとんどの場合、複数の数値がハッシュ関数を介して 1 つの数値にマップされます!

ジッパー方式

ジッパー方式の原理

これを行うには, 各値 k の背後にリンクされたリストを作成して、これらの同じマッピングのキー値を保存する必要があります. この方法はジッパー方法とも呼ばれます.

この問題を解決する方法はいくつかありますが、この状況をできるだけ回避する必要があるため、1000 に近い素数 N、ここでは 1003 が必要です。この数値を取得したら、x をこの数値 N で割って法を求めます。しかし、別の問題があります. モジュラスを正の数にしようとするので、負の数の余りに注意する必要があります. つまり、負の数のモジュラスを正の数に変換する必要があります.その理由は、モジュラスを N 自体に追加してから、モジュラスを取るからです。


using namespace std;

const int N = 1003;

int Hash_func(int x)
{
	return (x % N + N) % N;
}

ハッシュテーブル挿入(zipper方式)

その後、ハッシュテーブルに数値を挿入する方法を検討できます。まず第一に、マップする可能性のある k のすべてのセットを格納する配列が必要です。ここで配列 h が作成されます。これは、1003 のマッピング ロケーション アドレスのみを持つように設定したためです。したがって、この配列のサイズも 1003 です。次に、ストアド プロシージャは、この配列の下にデータの文字列をぶら下げていると理解できます。たとえば、データ x があり、それをハッシュ関数に渡し、位置 k を取得します。次に、配列 h で k とマークされた位置を見つけ、このデータをその下に吊るし、「吊るす」プロセスを実行します。単一リンクリストを使用して行われます。

コードに対応して、最初に h 配列、単一リンク リスト ポインター配列 Node、単一リンク リスト値配列 val、および現在の添字インデックスを作成し、次にアドレス値を取得してその下にノードを作成し、次に配列を作成します。 h[k] は片方向リストの先頭ノードに相当します。そのため、h 配列のすべての値を初期化し、値を -1 に割り当てる必要があります (片方向リストの先頭ノードは、他のノードが作成されていないときに添え字を指すため、次のように設定します)。デフォルトでは -1)。

using namespace std;
const int N = 1003;
int h[N], Node[N],val[N],index;

void Insert(int x)
{
	int n = Hash_func(x);
	val[index] = x;
	Node[index] = h[n];
	h[n] = index;
	index++;
}


int main()
{
	memset(h, -1, sizeof(h));
	return 0;
}

ハッシュテーブルクエリ(ジッパー方式)

 ハッシュ テーブルのクエリ操作については、単方向リンク リストのトラバーサル クエリを参照できます。まず、ハッシュ関数を介してチェックする番号の対応するアドレスを取得し、対応するアドレスの下にあるすべてのノードをトラバースする必要があります。検索する必要がある値と同じ値を持つノードが存在する場合は true を返し、適切な値が見つからない場合は false (つまり、見つからない) を返します。

bool find(int x)
{
	int n = Hash_func(x);
	for(int i =h[n];i!=-1;i=Node[i])
	{
		if (val[i] == x)
			return true;
	}
	return false;
}

オープンアドレッシング

ジッパー方式に加えて、ハッシュ テーブルの競合を解決する別の方式、つまりオープン アドレッシング方式を見つけることもできます。オープン アドレス方式は、配列の各要素の後に 1 つのリンク リストを作成してキー値を格納するジッパー方式とは異なります。キー値を格納するために使用できる配列を開くだけなので、ハッシュの衝突をどのように処理するのでしょうか?

オープン アドレッシングの原則

まず、大きな配列を開きます。通常は、あらかじめ決められた最大サイズの 2 倍または 3 倍です。キー値 9 があった場合、ハッシュ関数を渡した後の数値は 1 ですが、k=1 の位置は、ハッシュ関数の計算値も 1 であるキー値 36 によって既に占められていることがわかります。後方に空いているポジションを探し続けます。そしてその後ろにはハッシュ関数の値が2のキー値54がありますが、3番目の位置は空なので、ここに格納できます。

同様に、ハッシュ関数の計算値が 1 である特定の数 x があるかどうかを調べる必要がある場合、まずハッシュ関数を使用して、配列内のハッシュ関数を満たす最初のキー値を見つけます。次に、この値を見つけて比較します探している数 x でそれ. そうでない場合は, x に等しいキー値を探し続けます. 空の位置を見つけた場合, この要件を判断できます. 見つかった値 x はもう存在しません. .

コードで実装しましょう:

まず、ハッシュ関数を記述し、1 次元配列を作成する必要があります。ここでは、元のサイズの 2 倍を作成します。しかし、ここで問題があります。グリッドが空かどうかをどのように判断すればよいのでしょうか? 配列が初期化されるため、他の値がいくつか格納されます。このため、キー値の有効範囲外の大きな数値を「null」参照として使用することにしました。最初は、この数値は 1 次元配列全体を満たしますが、この時点でこの数値は「空」であると見なすことができます。そのため、変数 is_Empty も設定します。

#include<iostream>
#include<cstring>


using namespace std;

const int N = 2003, is_Empty = 10000;
int h[N];

int hash_func(int x)
{
	return (x % N + N) % N;
}

 以下は、クエリ操作を実行する検索関数を実装する必要があります。ここでもハッシュ関数を呼び出してアドレスを取得していますが、重要なステップは、while ループを介してキー値 x が存在するかどうかを判断することです。while ループの終了条件は 2 つあります: 1 つは x (h[k]==x) を見つけたこと、もう 1 つは h[k] の場所が「空」であることですプリセット)、この時点でこの x は存在しません。もう一度見つけられない場合は、適切な空席を返すことを選択します (ブール変数などを返すのではなく、後の挿入操作に役立ちます)。

int find(int x)
{
	int k = hash_func(x);
	while (h[k] != is_Empty && h[k] != x )
	{
		k++;
		if (k == N) 
			k = 0;
	}
	return k;
}

以下は挿入操作です. find 関数が x を格納するのに適した場所を返すように設定したので, この値を Insert 関数の添え字 k の場所に直接割り当てることができます.

void Insert(int x)
{
	int k = find(x);
	h[k] = x;
}

 全体的なコード:

#include<iostream>
#include<cstring>


using namespace std;

const int N = 2003, is_Empty = 10000;
int h[N];

int hash_func(int x)
{
	return (x % N + N) % N;
}

int find(int x)
{
	int k = hash_func(x);
	while (h[k] != is_Empty && h[k] != x )
	{
		k++;
		if (k == N) 
			k = 0;
	}
	return k;
}

void Insert(int x)
{
	int k = find(x);
	h[k] = x;
}


int main()
{
	for (int i = 0; i < N; i++)
		h[i] = is_Empty;
	Insert(10);
	return 0;
}

参考文献

参考1

参考2

おすすめ

転載: blog.csdn.net/m0_61151031/article/details/128968625