mirror of
https://github.com/krahets/hello-algo.git
synced 2026-04-10 06:10:19 +08:00
592 lines
21 KiB
Markdown
Executable File
592 lines
21 KiB
Markdown
Executable File
# Hash Table
|
|
|
|
A <u>hash table</u>, also known as a <u>hash map</u>, establishes a mapping between keys `key` and values `value`, enabling efficient element retrieval. Specifically, when we input a key `key` into a hash table, we can retrieve the corresponding value `value` in $O(1)$ time.
|
|
|
|
As shown in the figure below, given $n$ students, each with two pieces of data: "name" and "student ID". If we want to implement a query function that "inputs a student ID and returns the corresponding name", we can use the hash table shown below.
|
|
|
|

|
|
|
|
In addition to hash tables, arrays and linked lists can also implement query functionality. Their efficiency comparison is shown in the following table.
|
|
|
|
- **Adding elements**: Simply add elements to the end of the array (linked list), using $O(1)$ time.
|
|
- **Querying elements**: Since the array (linked list) is unordered, all elements need to be traversed, using $O(n)$ time.
|
|
- **Deleting elements**: The element must first be located, then deleted from the array (linked list), using $O(n)$ time.
|
|
|
|
<p align="center"> Table <id> Comparison of element query efficiency </p>
|
|
|
|
| | Array | Linked List | Hash Table |
|
|
| --------------- | ------ | ----------- | ---------- |
|
|
| Find element | $O(n)$ | $O(n)$ | $O(1)$ |
|
|
| Add element | $O(1)$ | $O(1)$ | $O(1)$ |
|
|
| Delete element | $O(n)$ | $O(n)$ | $O(1)$ |
|
|
|
|
As observed, **the time complexity for insertion, deletion, search, and modification operations in a hash table is $O(1)$**, which is very efficient.
|
|
|
|
## Common Hash Table Operations
|
|
|
|
Common operations on hash tables include: initialization, query operations, adding key-value pairs, and deleting key-value pairs. Example code is as follows:
|
|
|
|
=== "Python"
|
|
|
|
```python title="hash_map.py"
|
|
# Initialize hash table
|
|
hmap: dict = {}
|
|
|
|
# Add operation
|
|
# Add key-value pair (key, value) to hash table
|
|
hmap[12836] = "XiaoHa"
|
|
hmap[15937] = "XiaoLuo"
|
|
hmap[16750] = "XiaoSuan"
|
|
hmap[13276] = "XiaoFa"
|
|
hmap[10583] = "XiaoYa"
|
|
|
|
# Query operation
|
|
# Input key into hash table to get value
|
|
name: str = hmap[15937]
|
|
|
|
# Delete operation
|
|
# Delete key-value pair (key, value) from hash table
|
|
hmap.pop(10583)
|
|
```
|
|
|
|
=== "C++"
|
|
|
|
```cpp title="hash_map.cpp"
|
|
/* Initialize hash table */
|
|
unordered_map<int, string> map;
|
|
|
|
/* Add operation */
|
|
// Add key-value pair (key, value) to hash table
|
|
map[12836] = "XiaoHa";
|
|
map[15937] = "XiaoLuo";
|
|
map[16750] = "XiaoSuan";
|
|
map[13276] = "XiaoFa";
|
|
map[10583] = "XiaoYa";
|
|
|
|
/* Query operation */
|
|
// Input key into hash table to get value
|
|
string name = map[15937];
|
|
|
|
/* Delete operation */
|
|
// Delete key-value pair (key, value) from hash table
|
|
map.erase(10583);
|
|
```
|
|
|
|
=== "Java"
|
|
|
|
```java title="hash_map.java"
|
|
/* Initialize hash table */
|
|
Map<Integer, String> map = new HashMap<>();
|
|
|
|
/* Add operation */
|
|
// Add key-value pair (key, value) to hash table
|
|
map.put(12836, "XiaoHa");
|
|
map.put(15937, "XiaoLuo");
|
|
map.put(16750, "XiaoSuan");
|
|
map.put(13276, "XiaoFa");
|
|
map.put(10583, "XiaoYa");
|
|
|
|
/* Query operation */
|
|
// Input key into hash table to get value
|
|
String name = map.get(15937);
|
|
|
|
/* Delete operation */
|
|
// Delete key-value pair (key, value) from hash table
|
|
map.remove(10583);
|
|
```
|
|
|
|
=== "C#"
|
|
|
|
```csharp title="hash_map.cs"
|
|
/* Initialize hash table */
|
|
Dictionary<int, string> map = new() {
|
|
/* Add operation */
|
|
// Add key-value pair (key, value) to hash table
|
|
{ 12836, "XiaoHa" },
|
|
{ 15937, "XiaoLuo" },
|
|
{ 16750, "XiaoSuan" },
|
|
{ 13276, "XiaoFa" },
|
|
{ 10583, "XiaoYa" }
|
|
};
|
|
|
|
/* Query operation */
|
|
// Input key into hash table to get value
|
|
string name = map[15937];
|
|
|
|
/* Delete operation */
|
|
// Delete key-value pair (key, value) from hash table
|
|
map.Remove(10583);
|
|
```
|
|
|
|
=== "Go"
|
|
|
|
```go title="hash_map_test.go"
|
|
/* Initialize hash table */
|
|
hmap := make(map[int]string)
|
|
|
|
/* Add operation */
|
|
// Add key-value pair (key, value) to hash table
|
|
hmap[12836] = "XiaoHa"
|
|
hmap[15937] = "XiaoLuo"
|
|
hmap[16750] = "XiaoSuan"
|
|
hmap[13276] = "XiaoFa"
|
|
hmap[10583] = "XiaoYa"
|
|
|
|
/* Query operation */
|
|
// Input key into hash table to get value
|
|
name := hmap[15937]
|
|
|
|
/* Delete operation */
|
|
// Delete key-value pair (key, value) from hash table
|
|
delete(hmap, 10583)
|
|
```
|
|
|
|
=== "Swift"
|
|
|
|
```swift title="hash_map.swift"
|
|
/* Initialize hash table */
|
|
var map: [Int: String] = [:]
|
|
|
|
/* Add operation */
|
|
// Add key-value pair (key, value) to hash table
|
|
map[12836] = "XiaoHa"
|
|
map[15937] = "XiaoLuo"
|
|
map[16750] = "XiaoSuan"
|
|
map[13276] = "XiaoFa"
|
|
map[10583] = "XiaoYa"
|
|
|
|
/* Query operation */
|
|
// Input key into hash table to get value
|
|
let name = map[15937]!
|
|
|
|
/* Delete operation */
|
|
// Delete key-value pair (key, value) from hash table
|
|
map.removeValue(forKey: 10583)
|
|
```
|
|
|
|
=== "JS"
|
|
|
|
```javascript title="hash_map.js"
|
|
/* Initialize hash table */
|
|
const map = new Map();
|
|
/* Add operation */
|
|
// Add key-value pair (key, value) to hash table
|
|
map.set(12836, 'XiaoHa');
|
|
map.set(15937, 'XiaoLuo');
|
|
map.set(16750, 'XiaoSuan');
|
|
map.set(13276, 'XiaoFa');
|
|
map.set(10583, 'XiaoYa');
|
|
|
|
/* Query operation */
|
|
// Input key into hash table to get value
|
|
let name = map.get(15937);
|
|
|
|
/* Delete operation */
|
|
// Delete key-value pair (key, value) from hash table
|
|
map.delete(10583);
|
|
```
|
|
|
|
=== "TS"
|
|
|
|
```typescript title="hash_map.ts"
|
|
/* Initialize hash table */
|
|
const map = new Map<number, string>();
|
|
/* Add operation */
|
|
// Add key-value pair (key, value) to hash table
|
|
map.set(12836, 'XiaoHa');
|
|
map.set(15937, 'XiaoLuo');
|
|
map.set(16750, 'XiaoSuan');
|
|
map.set(13276, 'XiaoFa');
|
|
map.set(10583, 'XiaoYa');
|
|
console.info('\nAfter adding, hash table is\nKey -> Value');
|
|
console.info(map);
|
|
|
|
/* Query operation */
|
|
// Input key into hash table to get value
|
|
let name = map.get(15937);
|
|
console.info('\nInput student ID 15937, queried name ' + name);
|
|
|
|
/* Delete operation */
|
|
// Delete key-value pair (key, value) from hash table
|
|
map.delete(10583);
|
|
console.info('\nAfter deleting 10583, hash table is\nKey -> Value');
|
|
console.info(map);
|
|
```
|
|
|
|
=== "Dart"
|
|
|
|
```dart title="hash_map.dart"
|
|
/* Initialize hash table */
|
|
Map<int, String> map = {};
|
|
|
|
/* Add operation */
|
|
// Add key-value pair (key, value) to hash table
|
|
map[12836] = "XiaoHa";
|
|
map[15937] = "XiaoLuo";
|
|
map[16750] = "XiaoSuan";
|
|
map[13276] = "XiaoFa";
|
|
map[10583] = "XiaoYa";
|
|
|
|
/* Query operation */
|
|
// Input key into hash table to get value
|
|
String name = map[15937];
|
|
|
|
/* Delete operation */
|
|
// Delete key-value pair (key, value) from hash table
|
|
map.remove(10583);
|
|
```
|
|
|
|
=== "Rust"
|
|
|
|
```rust title="hash_map.rs"
|
|
use std::collections::HashMap;
|
|
|
|
/* Initialize hash table */
|
|
let mut map: HashMap<i32, String> = HashMap::new();
|
|
|
|
/* Add operation */
|
|
// Add key-value pair (key, value) to hash table
|
|
map.insert(12836, "XiaoHa".to_string());
|
|
map.insert(15937, "XiaoLuo".to_string());
|
|
map.insert(16750, "XiaoSuan".to_string());
|
|
map.insert(13279, "XiaoFa".to_string());
|
|
map.insert(10583, "XiaoYa".to_string());
|
|
|
|
/* Query operation */
|
|
// Input key into hash table to get value
|
|
let _name: Option<&String> = map.get(&15937);
|
|
|
|
/* Delete operation */
|
|
// Delete key-value pair (key, value) from hash table
|
|
let _removed_value: Option<String> = map.remove(&10583);
|
|
```
|
|
|
|
=== "C"
|
|
|
|
```c title="hash_map.c"
|
|
// C does not provide a built-in hash table
|
|
```
|
|
|
|
=== "Kotlin"
|
|
|
|
```kotlin title="hash_map.kt"
|
|
/* Initialize hash table */
|
|
val map = HashMap<Int,String>()
|
|
|
|
/* Add operation */
|
|
// Add key-value pair (key, value) to hash table
|
|
map[12836] = "XiaoHa"
|
|
map[15937] = "XiaoLuo"
|
|
map[16750] = "XiaoSuan"
|
|
map[13276] = "XiaoFa"
|
|
map[10583] = "XiaoYa"
|
|
|
|
/* Query operation */
|
|
// Input key into hash table to get value
|
|
val name = map[15937]
|
|
|
|
/* Delete operation */
|
|
// Delete key-value pair (key, value) from hash table
|
|
map.remove(10583)
|
|
```
|
|
|
|
=== "Ruby"
|
|
|
|
```ruby title="hash_map.rb"
|
|
# Initialize hash table
|
|
hmap = {}
|
|
|
|
# Add operation
|
|
# Add key-value pair (key, value) to hash table
|
|
hmap[12836] = "XiaoHa"
|
|
hmap[15937] = "XiaoLuo"
|
|
hmap[16750] = "XiaoSuan"
|
|
hmap[13276] = "XiaoFa"
|
|
hmap[10583] = "XiaoYa"
|
|
|
|
# Query operation
|
|
# Input key into hash table to get value
|
|
name = hmap[15937]
|
|
|
|
# Delete operation
|
|
# Delete key-value pair (key, value) from hash table
|
|
hmap.delete(10583)
|
|
```
|
|
|
|
??? pythontutor "Visualized Execution"
|
|
|
|
https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%93%88%E5%B8%8C%E8%A1%A8%0A%20%20%20%20hmap%20%3D%20%7B%7D%0A%20%20%20%20%0A%20%20%20%20%23%20%E6%B7%BB%E5%8A%A0%E6%93%8D%E4%BD%9C%0A%20%20%20%20%23%20%E5%9C%A8%E5%93%88%E5%B8%8C%E8%A1%A8%E4%B8%AD%E6%B7%BB%E5%8A%A0%E9%94%AE%E5%80%BC%E5%AF%B9%20%28key,%20value%29%0A%20%20%20%20hmap%5B12836%5D%20%3D%20%22%E5%B0%8F%E5%93%88%22%0A%20%20%20%20hmap%5B15937%5D%20%3D%20%22%E5%B0%8F%E5%95%B0%22%0A%20%20%20%20hmap%5B16750%5D%20%3D%20%22%E5%B0%8F%E7%AE%97%22%0A%20%20%20%20hmap%5B13276%5D%20%3D%20%22%E5%B0%8F%E6%B3%95%22%0A%20%20%20%20hmap%5B10583%5D%20%3D%20%22%E5%B0%8F%E9%B8%AD%22%0A%20%20%20%20%0A%20%20%20%20%23%20%E6%9F%A5%E8%AF%A2%E6%93%8D%E4%BD%9C%0A%20%20%20%20%23%20%E5%90%91%E5%93%88%E5%B8%8C%E8%A1%A8%E4%B8%AD%E8%BE%93%E5%85%A5%E9%94%AE%20key%20%EF%BC%8C%E5%BE%97%E5%88%B0%E5%80%BC%20value%0A%20%20%20%20name%20%3D%20hmap%5B15937%5D%0A%20%20%20%20%0A%20%20%20%20%23%20%E5%88%A0%E9%99%A4%E6%93%8D%E4%BD%9C%0A%20%20%20%20%23%20%E5%9C%A8%E5%93%88%E5%B8%8C%E8%A1%A8%E4%B8%AD%E5%88%A0%E9%99%A4%E9%94%AE%E5%80%BC%E5%AF%B9%20%28key,%20value%29%0A%20%20%20%20hmap.pop%2810583%29&cumulative=false&curInstr=2&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false
|
|
|
|
There are three common ways to traverse a hash table: traversing key-value pairs, traversing keys, and traversing values. Example code is as follows:
|
|
|
|
=== "Python"
|
|
|
|
```python title="hash_map.py"
|
|
# Traverse hash table
|
|
# Traverse key-value pairs key->value
|
|
for key, value in hmap.items():
|
|
print(key, "->", value)
|
|
# Traverse keys only
|
|
for key in hmap.keys():
|
|
print(key)
|
|
# Traverse values only
|
|
for value in hmap.values():
|
|
print(value)
|
|
```
|
|
|
|
=== "C++"
|
|
|
|
```cpp title="hash_map.cpp"
|
|
/* Traverse hash table */
|
|
// Traverse key-value pairs key->value
|
|
for (auto kv: map) {
|
|
cout << kv.first << " -> " << kv.second << endl;
|
|
}
|
|
// Traverse using iterator key->value
|
|
for (auto iter = map.begin(); iter != map.end(); iter++) {
|
|
cout << iter->first << "->" << iter->second << endl;
|
|
}
|
|
```
|
|
|
|
=== "Java"
|
|
|
|
```java title="hash_map.java"
|
|
/* Traverse hash table */
|
|
// Traverse key-value pairs key->value
|
|
for (Map.Entry<Integer, String> kv: map.entrySet()) {
|
|
System.out.println(kv.getKey() + " -> " + kv.getValue());
|
|
}
|
|
// Traverse keys only
|
|
for (int key: map.keySet()) {
|
|
System.out.println(key);
|
|
}
|
|
// Traverse values only
|
|
for (String val: map.values()) {
|
|
System.out.println(val);
|
|
}
|
|
```
|
|
|
|
=== "C#"
|
|
|
|
```csharp title="hash_map.cs"
|
|
/* Traverse hash table */
|
|
// Traverse key-value pairs Key->Value
|
|
foreach (var kv in map) {
|
|
Console.WriteLine(kv.Key + " -> " + kv.Value);
|
|
}
|
|
// Traverse keys only
|
|
foreach (int key in map.Keys) {
|
|
Console.WriteLine(key);
|
|
}
|
|
// Traverse values only
|
|
foreach (string val in map.Values) {
|
|
Console.WriteLine(val);
|
|
}
|
|
```
|
|
|
|
=== "Go"
|
|
|
|
```go title="hash_map_test.go"
|
|
/* Traverse hash table */
|
|
// Traverse key-value pairs key->value
|
|
for key, value := range hmap {
|
|
fmt.Println(key, "->", value)
|
|
}
|
|
// Traverse keys only
|
|
for key := range hmap {
|
|
fmt.Println(key)
|
|
}
|
|
// Traverse values only
|
|
for _, value := range hmap {
|
|
fmt.Println(value)
|
|
}
|
|
```
|
|
|
|
=== "Swift"
|
|
|
|
```swift title="hash_map.swift"
|
|
/* Traverse hash table */
|
|
// Traverse key-value pairs Key->Value
|
|
for (key, value) in map {
|
|
print("\(key) -> \(value)")
|
|
}
|
|
// Traverse keys only
|
|
for key in map.keys {
|
|
print(key)
|
|
}
|
|
// Traverse values only
|
|
for value in map.values {
|
|
print(value)
|
|
}
|
|
```
|
|
|
|
=== "JS"
|
|
|
|
```javascript title="hash_map.js"
|
|
/* Traverse hash table */
|
|
console.info('\nTraverse key-value pairs Key->Value');
|
|
for (const [k, v] of map.entries()) {
|
|
console.info(k + ' -> ' + v);
|
|
}
|
|
console.info('\nTraverse keys only Key');
|
|
for (const k of map.keys()) {
|
|
console.info(k);
|
|
}
|
|
console.info('\nTraverse values only Value');
|
|
for (const v of map.values()) {
|
|
console.info(v);
|
|
}
|
|
```
|
|
|
|
=== "TS"
|
|
|
|
```typescript title="hash_map.ts"
|
|
/* Traverse hash table */
|
|
console.info('\nTraverse key-value pairs Key->Value');
|
|
for (const [k, v] of map.entries()) {
|
|
console.info(k + ' -> ' + v);
|
|
}
|
|
console.info('\nTraverse keys only Key');
|
|
for (const k of map.keys()) {
|
|
console.info(k);
|
|
}
|
|
console.info('\nTraverse values only Value');
|
|
for (const v of map.values()) {
|
|
console.info(v);
|
|
}
|
|
```
|
|
|
|
=== "Dart"
|
|
|
|
```dart title="hash_map.dart"
|
|
/* Traverse hash table */
|
|
// Traverse key-value pairs Key->Value
|
|
map.forEach((key, value) {
|
|
print('$key -> $value');
|
|
});
|
|
|
|
// Traverse keys only
|
|
map.keys.forEach((key) {
|
|
print(key);
|
|
});
|
|
|
|
// Traverse values only
|
|
map.values.forEach((value) {
|
|
print(value);
|
|
});
|
|
```
|
|
|
|
=== "Rust"
|
|
|
|
```rust title="hash_map.rs"
|
|
/* Traverse hash table */
|
|
// Traverse key-value pairs Key->Value
|
|
for (key, value) in &map {
|
|
println!("{key} -> {value}");
|
|
}
|
|
|
|
// Traverse keys only
|
|
for key in map.keys() {
|
|
println!("{key}");
|
|
}
|
|
|
|
// Traverse values only
|
|
for value in map.values() {
|
|
println!("{value}");
|
|
}
|
|
```
|
|
|
|
=== "C"
|
|
|
|
```c title="hash_map.c"
|
|
// C does not provide a built-in hash table
|
|
```
|
|
|
|
=== "Kotlin"
|
|
|
|
```kotlin title="hash_map.kt"
|
|
/* Traverse hash table */
|
|
// Traverse key-value pairs key->value
|
|
for ((key, value) in map) {
|
|
println("$key -> $value")
|
|
}
|
|
// Traverse keys only
|
|
for (key in map.keys) {
|
|
println(key)
|
|
}
|
|
// Traverse values only
|
|
for (_val in map.values) {
|
|
println(_val)
|
|
}
|
|
```
|
|
|
|
=== "Ruby"
|
|
|
|
```ruby title="hash_map.rb"
|
|
# Traverse hash table
|
|
# Traverse key-value pairs key->value
|
|
hmap.entries.each { |key, value| puts "#{key} -> #{value}" }
|
|
|
|
# Traverse keys only
|
|
hmap.keys.each { |key| puts key }
|
|
|
|
# Traverse values only
|
|
hmap.values.each { |val| puts val }
|
|
```
|
|
|
|
??? pythontutor "Visualized Execution"
|
|
|
|
https://pythontutor.com/render.html#code=%22%22%22Driver%20Code%22%22%22%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%93%88%E5%B8%8C%E8%A1%A8%0A%20%20%20%20hmap%20%3D%20%7B%7D%0A%20%20%20%20%0A%20%20%20%20%23%20%E6%B7%BB%E5%8A%A0%E6%93%8D%E4%BD%9C%0A%20%20%20%20%23%20%E5%9C%A8%E5%93%88%E5%B8%8C%E8%A1%A8%E4%B8%AD%E6%B7%BB%E5%8A%A0%E9%94%AE%E5%80%BC%E5%AF%B9%20%28key,%20value%29%0A%20%20%20%20hmap%5B12836%5D%20%3D%20%22%E5%B0%8F%E5%93%88%22%0A%20%20%20%20hmap%5B15937%5D%20%3D%20%22%E5%B0%8F%E5%95%B0%22%0A%20%20%20%20hmap%5B16750%5D%20%3D%20%22%E5%B0%8F%E7%AE%97%22%0A%20%20%20%20hmap%5B13276%5D%20%3D%20%22%E5%B0%8F%E6%B3%95%22%0A%20%20%20%20hmap%5B10583%5D%20%3D%20%22%E5%B0%8F%E9%B8%AD%22%0A%20%20%20%20%0A%20%20%20%20%23%20%E9%81%8D%E5%8E%86%E5%93%88%E5%B8%8C%E8%A1%A8%0A%20%20%20%20%23%20%E9%81%8D%E5%8E%86%E9%94%AE%E5%80%BC%E5%AF%B9%20key-%3Evalue%0A%20%20%20%20for%20key,%20value%20in%20hmap.items%28%29%3A%0A%20%20%20%20%20%20%20%20print%28key,%20%22-%3E%22,%20value%29%0A%20%20%20%20%23%20%E5%8D%95%E7%8B%AC%E9%81%8D%E5%8E%86%E9%94%AE%20key%0A%20%20%20%20for%20key%20in%20hmap.keys%28%29%3A%0A%20%20%20%20%20%20%20%20print%28key%29%0A%20%20%20%20%23%20%E5%8D%95%E7%8B%AC%E9%81%8D%E5%8E%86%E5%80%BC%20value%0A%20%20%20%20for%20value%20in%20hmap.values%28%29%3A%0A%20%20%20%20%20%20%20%20print%28value%29&cumulative=false&curInstr=8&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false
|
|
|
|
## Simple Hash Table Implementation
|
|
|
|
Let's first consider the simplest case: **implementing a hash table using only an array**. In a hash table, each empty position in the array is called a <u>bucket</u>, and each bucket can store a key-value pair. Therefore, the query operation is to find the bucket corresponding to `key` and retrieve the `value` from the bucket.
|
|
|
|
So how do we locate the corresponding bucket based on `key`? This is achieved through a <u>hash function</u>. The role of the hash function is to map a larger input space to a smaller output space. In a hash table, the input space is all `key`s, and the output space is all buckets (array indices). In other words, given a `key`, **we can use the hash function to obtain the storage location of the key-value pair corresponding to that `key` in the array**.
|
|
|
|
When inputting a `key`, the hash function's calculation process consists of the following two steps:
|
|
|
|
1. Calculate the hash value through a hash algorithm `hash()`.
|
|
2. Take the modulo of the hash value by the number of buckets (array length) `capacity` to obtain the bucket (array index) `index` corresponding to that `key`.
|
|
|
|
```shell
|
|
index = hash(key) % capacity
|
|
```
|
|
|
|
Subsequently, we can use `index` to access the corresponding bucket in the hash table and retrieve the `value`.
|
|
|
|
Assuming the array length is `capacity = 100` and the hash algorithm is `hash(key) = key`, the hash function becomes `key % 100`. The figure below shows the working principle of the hash function using `key` as student ID and `value` as name.
|
|
|
|

|
|
|
|
The following code implements a simple hash table. Here, we encapsulate `key` and `value` into a class `Pair` to represent a key-value pair.
|
|
|
|
```src
|
|
[file]{array_hash_map}-[class]{array_hash_map}-[func]{}
|
|
```
|
|
|
|
## Hash Collision and Resizing
|
|
|
|
Fundamentally, the role of a hash function is to map the input space consisting of all `key`s to the output space consisting of all array indices, and the input space is often much larger than the output space. Therefore, **theoretically there must be cases where "multiple inputs correspond to the same output"**.
|
|
|
|
For the hash function in the above example, when the input `key`s have the same last two digits, the hash function produces the same output. For example, when querying two students with IDs 12836 and 20336, we get:
|
|
|
|
```shell
|
|
12836 % 100 = 36
|
|
20336 % 100 = 36
|
|
```
|
|
|
|
As shown in the figure below, two student IDs point to the same name, which is obviously incorrect. We call this situation where multiple inputs correspond to the same output a <u>hash collision</u>.
|
|
|
|

|
|
|
|
It's easy to see that the larger the hash table capacity $n$, the lower the probability that multiple `key`s will be assigned to the same bucket, and the fewer collisions. Therefore, **we can reduce hash collisions by expanding the hash table**.
|
|
|
|
As shown in the figure below, before expansion, the key-value pairs `(136, A)` and `(236, D)` collided, but after expansion, the collision disappears.
|
|
|
|

|
|
|
|
Similar to array expansion, hash table expansion requires migrating all key-value pairs from the original hash table to the new hash table, which is very time-consuming. Moreover, since the hash table capacity `capacity` changes, we need to recalculate the storage locations of all key-value pairs through the hash function, further increasing the computational overhead of the expansion process. For this reason, programming languages typically reserve a sufficiently large hash table capacity to prevent frequent expansion.
|
|
|
|
The <u>load factor</u> is an important concept for hash tables. It is defined as the number of elements in the hash table divided by the number of buckets, and is used to measure the severity of hash collisions. **It is also commonly used as a trigger condition for hash table expansion**. For example, in Java, when the load factor exceeds $0.75$, the system will expand the hash table to $2$ times its original size.
|