ハッシュ テーブルは、ハッシュ関数を使用してデータを整理するデータ構造であり、高速な挿入と検索をサポートします。
ハッシュ テーブル (ハッシュ テーブルとも呼ばれる) は、ハッシュ関数を利用してキーをバケット アドレスにマッピングすることで機能します。より正確には、
1. まず、連続した物理アドレスを持つ一定の長さのバケット配列を開きます;
2. 新しいキーを挿入すると、ハッシュ関数がキーをどのバケットに割り当てるかを決定し、そのキーを格納場所に割り当てます。対応するバケット;
3. キーを検索する場合、ハッシュ テーブルはハッシュ関数を使用して対応するバケットを見つけ、そのバケット内を検索します。
負荷係数はフィル係数とも呼ばれ、ハッシュ テーブルの重要なパラメータであり、ハッシュ テーブルの充足度を反映します。
バケットの総数に対する実際に使用されるバケットの数の比率は、負荷率と呼ばれます。
ハッシュ関数
ハッシュ関数はハッシュ テーブルの最も重要なコンポーネントであり、キーを特定のバケットにマッピングするために使用されます。ハッシュ関数として y = x % 5 を使用します。ここで、x はキー値、y はマッピング後の対応するバケットのインデックスです。
紛争解決
通常、ハッシュ関数はキーのアドレス空間を圧縮する役割を果たしますが、キーのアドレス空間をS、バケットのアドレス空間をTとすると、S≫Tとなります。
そのため、マッピング後は必然的に異なるデータが同じバケットに割り当てられることになり、競合が発生します。
線形ヒューリスティック
線形ヒューリスティック法とは、オープンアドレッシング法の一種で、キー key を挿入する際、バケット単位のバケット[hash(key)] が既に占有されていることが判明した場合、線形探索を行うことを意味します。使用可能な空きスペースが見つかるまで、下方向に実行されます。具体的には、i 回目の試行後のバケット単位は、bucket[(hash(key)+i) mod M], i=1,2,3… となる必要があります。
チェーンアドレス方式
競合を解決するもう 1 つの方法は、バケット内の競合するキーを連結してリンク リストにすることです。
焼き直し
リハッシュの典型的な用途はダブル ハッシュです。つまり、競合が発生した場合、競合を回避するために別のハッシュ関数が使用されます。
ただし、二重ハッシュにはいくつかの問題もあります。
1. 線形ヒューリスティックと比較して、二重ハッシュはより多くの時間を消費します。
2. 二重ハッシュの場合、削除すると問題が複雑になるため、論理的な削除の数が多すぎる場合は、ハッシュ テーブルを再構築する必要があります。
公共流出区域法
パブリックオーバーフロー領域方式はその名のとおり、パブリックオーバーフロー領域として別のハッシュテーブルdict_overflowを作成し、競合が発生した場合にはキーをハッシュテーブルに保存する方式です。
単純なハッシュセット
#define MAX_LEN 100000 // 初始化桶的数量
class MyHashSet {
private:
vector<int> set[MAX_LEN]; // 使用数组实现哈希集合
/** 返回对应的桶的索引 */
int getIndex(int key) {
return key % MAX_LEN;
}
/** 在特定的桶中搜索键,如果该键不存在则返回 -1 */
int getPos(int key, int index) {
// 每个桶中包含一个列表,遍历所有桶中的元素来寻找特定的键
for (int i = 0; i < set[index].size(); ++i) {
if (set[index][i] == key) {
return i;
}
}
return -1;
}
public:
MyHashSet() {
}
void add(int key) {
int index = getIndex(key);
int pos = getPos(key, index);
if (pos < 0) {
// 如果键不存在,则添加
set[index].push_back(key);
}
}
void remove(int key) {
int index = getIndex(key);
int pos = getPos(key, index);
if (pos >= 0) {
// 如果键存在,则删除
set[index].erase(set[index].begin() + pos);
}
}
bool contains(int key) {
int index = getIndex(key);
int pos = getPos(key, index);
return pos >= 0;
}
};
単純なハッシュマップ
#define MAX_LEN 100000 // 初始化桶的数量
class MyHashMap {
private:
vector<pair<int, int>> map[MAX_LEN]; // 使用数组实现哈希集合
/** 返回指定桶的索引 */
int getIndex(int key) {
return key % MAX_LEN;
}
/** 在桶中搜索键,如果不存在则返回 -1 */
int getPos(int key, int index) {
// 每个桶包含一个数组,遍历桶中的所有元素来查找指定的 key
for (int i = 0; i < map[index].size(); ++i) {
if (map[index][i].first == key) {
return i;
}
}
return -1;
}
public:
MyHashMap() {
}
/** value 始终为正 */
void put(int key, int value) {
int index = getIndex(key);
int pos = getPos(key, index);
if (pos < 0) {
map[index].push_back(make_pair(key, value));
} else {
map[index][pos].second = value;
}
}
/** 如果存在映射关系,则返回 value,否则返回 -1 */
int get(int key) {
int index = getIndex(key);
int pos = getPos(key, index);
if (pos < 0) {
return -1;
} else {
return map[index][pos].second;
}
}
/** 如果存在 key 的映射,则删除该映射关系 */
void remove(int key) {
int index = getIndex(key);
int pos = getPos(key, index);
if (pos >= 0) {
map[index].erase(map[index].begin() + pos);
}
}
};