This commit is contained in:
krahets
2023-08-17 05:12:05 +08:00
parent f0826da7f6
commit 97c532b228
67 changed files with 1481 additions and 1066 deletions
+43 -11
View File
@@ -10,7 +10,7 @@ comments: true
![哈希冲突的最佳与最差情况](hash_algorithm.assets/hash_collision_best_worst_condition.png)
<p align="center"> Fig. 哈希冲突的最佳与最差情况 </p>
<p align="center"> 图:哈希冲突的最佳与最差情况 </p>
**键值对的分布情况由哈希函数决定**。回忆哈希函数的计算步骤,先计算哈希值,再对数组长度取模:
@@ -439,13 +439,45 @@ index = hash(key) % capacity
=== "Dart"
```dart title="simple_hash.dart"
[class]{}-[func]{add_hash}
/* 加法哈希 */
int addHash(String key) {
int hash = 0;
final int MODULUS = 1000000007;
for (int i = 0; i < key.length; i++) {
hash = (hash + key.codeUnitAt(i)) % MODULUS;
}
return hash;
}
[class]{}-[func]{mul_hash}
/* 乘法哈希 */
int mulHash(String key) {
int hash = 0;
final int MODULUS = 1000000007;
for (int i = 0; i < key.length; i++) {
hash = (31 * hash + key.codeUnitAt(i)) % MODULUS;
}
return hash;
}
[class]{}-[func]{xor_hash}
/* 异或哈希 */
int xorHash(String key) {
int hash = 0;
final int MODULUS = 1000000007;
for (int i = 0; i < key.length; i++) {
hash ^= key.codeUnitAt(i);
}
return hash & MODULUS;
}
[class]{}-[func]{rot_hash}
/* 旋转哈希 */
int rotHash(String key) {
int hash = 0;
final int MODULUS = 1000000007;
for (int i = 0; i < key.length; i++) {
hash = ((hash << 4) ^ (hash >> 28) ^ key.codeUnitAt(i)) % MODULUS;
}
return hash;
}
```
=== "Rust"
@@ -584,7 +616,7 @@ $$
# 布尔量 True 的哈希值为 1
dec = 3.14159
hash_dec = hash(dec)
hash_dec = hash(dec)
# 小数 3.14159 的哈希值为 326484311674566659
str = "Hello 算法"
@@ -692,23 +724,23 @@ $$
int num = 3;
int hashNum = num.hashCode;
// 整数 3 的哈希值为 34803
bool bol = true;
int hashBol = bol.hashCode;
// 布尔值 true 的哈希值为 1231
double dec = 3.14159;
int hashDec = dec.hashCode;
// 小数 3.14159 的哈希值为 2570631074981783
String str = "Hello 算法";
int hashStr = str.hashCode;
// 字符串 Hello 算法 的哈希值为 468167534
List arr = [12836, "小哈"];
int hashArr = arr.hashCode;
// 数组 [12836, 小哈] 的哈希值为 976512528
ListNode obj = new ListNode(0);
int hashObj = obj.hashCode;
// 节点对象 Instance of 'ListNode' 的哈希值为 1033450432
+3 -3
View File
@@ -19,7 +19,7 @@ comments: true
![链式地址哈希表](hash_collision.assets/hash_table_chaining.png)
<p align="center"> Fig. 链式地址哈希表 </p>
<p align="center"> 图:链式地址哈希表 </p>
链式地址下,哈希表的操作方法包括:
@@ -542,7 +542,7 @@ comments: true
for (let i = 0; i < bucket.length; i++) {
if (bucket[i].key === key) {
bucket.splice(i, 1);
this.size--;
this.#size--;
break;
}
}
@@ -1165,7 +1165,7 @@ comments: true
![线性探测](hash_collision.assets/hash_table_linear_probing.png)
<p align="center"> Fig. 线性探测 </p>
<p align="center"> 图:线性探测 </p>
然而,线性探测存在以下缺陷:
+4 -4
View File
@@ -10,7 +10,7 @@ comments: true
![哈希表的抽象表示](hash_map.assets/hash_table_lookup.png)
<p align="center"> Fig. 哈希表的抽象表示 </p>
<p align="center"> 图:哈希表的抽象表示 </p>
除哈希表外,我们还可以使用数组或链表实现查询功能。若将学生数据看作数组(链表)元素,则有:
@@ -464,7 +464,7 @@ index = hash(key) % capacity
![哈希函数工作原理](hash_map.assets/hash_function.png)
<p align="center"> Fig. 哈希函数工作原理 </p>
<p align="center"> 图:哈希函数工作原理 </p>
以下代码实现了一个简单哈希表。其中,我们将 `key` 和 `value` 封装成一个类 `Pair` ,以表示键值对。
@@ -1501,13 +1501,13 @@ index = hash(key) % capacity
![哈希冲突示例](hash_map.assets/hash_collision.png)
<p align="center"> Fig. 哈希冲突示例 </p>
<p align="center"> 图:哈希冲突示例 </p>
容易想到,哈希表容量 $n$ 越大,多个 `key` 被分配到同一个桶中的概率就越低,冲突就越少。因此,**我们可以通过扩容哈希表来减少哈希冲突**。如下图所示,扩容前键值对 `(136, A)` 和 `(236, D)` 发生冲突,扩容后冲突消失。
![哈希表扩容](hash_map.assets/hash_table_reshash.png)
<p align="center"> Fig. 哈希表扩容 </p>
<p align="center"> 图:哈希表扩容 </p>
类似于数组扩容,哈希表扩容需将所有键值对从原哈希表迁移至新哈希表,非常耗时。并且由于哈希表容量 `capacity` 改变,我们需要通过哈希函数来重新计算所有键值对的存储位置,这进一步提高了扩容过程的计算开销。为此,编程语言通常会预留足够大的哈希表容量,防止频繁扩容。