This commit is contained in:
krahets
2024-05-01 07:30:10 +08:00
parent 583d338530
commit a08cd961b3
64 changed files with 227 additions and 227 deletions
+2 -2
View File
@@ -6,7 +6,7 @@ comments: true
The previous two sections introduced the working principle of hash tables and the methods to handle hash collisions. However, both open addressing and chaining can **only ensure that the hash table functions normally when collisions occur, but cannot reduce the frequency of hash collisions**.
If hash collisions occur too frequently, the performance of the hash table will deteriorate drastically. As shown in the Figure 6-8 , for a chaining hash table, in the ideal case, the key-value pairs are evenly distributed across the buckets, achieving optimal query efficiency; in the worst case, all key-value pairs are stored in the same bucket, degrading the time complexity to $O(n)$.
If hash collisions occur too frequently, the performance of the hash table will deteriorate drastically. As shown in Figure 6-8, for a chaining hash table, in the ideal case, the key-value pairs are evenly distributed across the buckets, achieving optimal query efficiency; in the worst case, all key-value pairs are stored in the same bucket, degrading the time complexity to $O(n)$.
![Ideal and worst cases of hash collisions](hash_algorithm.assets/hash_collision_best_worst_condition.png){ class="animation-figure" }
@@ -693,7 +693,7 @@ It is not hard to see that the simple hash algorithms mentioned above are quite
In practice, we usually use some standard hash algorithms, such as MD5, SHA-1, SHA-2, and SHA-3. They can map input data of any length to a fixed-length hash value.
Over the past century, hash algorithms have been in a continuous process of upgrading and optimization. Some researchers strive to improve the performance of hash algorithms, while others, including hackers, are dedicated to finding security issues in hash algorithms. The Table 6-2 shows hash algorithms commonly used in practical applications.
Over the past century, hash algorithms have been in a continuous process of upgrading and optimization. Some researchers strive to improve the performance of hash algorithms, while others, including hackers, are dedicated to finding security issues in hash algorithms. Table 6-2 shows hash algorithms commonly used in practical applications.
- MD5 and SHA-1 have been successfully attacked multiple times and are thus abandoned in various security applications.
- SHA-2 series, especially SHA-256, is one of the most secure hash algorithms to date, with no successful attacks reported, hence commonly used in various security applications and protocols.
+3 -3
View File
@@ -15,7 +15,7 @@ There are mainly two methods for improving the structure of hash tables: "Separa
## 6.2.1   Separate chaining
In the original hash table, each bucket can store only one key-value pair. "Separate chaining" transforms individual elements into a linked list, with key-value pairs as list nodes, storing all colliding key-value pairs in the same list. The Figure 6-5 shows an example of a hash table with separate chaining.
In the original hash table, each bucket can store only one key-value pair. "Separate chaining" transforms individual elements into a linked list, with key-value pairs as list nodes, storing all colliding key-value pairs in the same list. Figure 6-5 shows an example of a hash table with separate chaining.
![Separate chaining hash table](hash_collision.assets/hash_table_chaining.png){ class="animation-figure" }
@@ -1547,7 +1547,7 @@ Linear probing uses a fixed-step linear search for probing, differing from ordin
- **Inserting elements**: Calculate the bucket index using the hash function. If the bucket already contains an element, linearly traverse forward from the conflict position (usually with a step size of $1$) until an empty bucket is found, then insert the element.
- **Searching for elements**: If a hash collision is found, use the same step size to linearly traverse forward until the corresponding element is found and return `value`; if an empty bucket is encountered, it means the target element is not in the hash table, so return `None`.
The Figure 6-6 shows the distribution of key-value pairs in an open addressing (linear probing) hash table. According to this hash function, keys with the same last two digits will be mapped to the same bucket. Through linear probing, they are stored consecutively in that bucket and the buckets below it.
Figure 6-6 shows the distribution of key-value pairs in an open addressing (linear probing) hash table. According to this hash function, keys with the same last two digits will be mapped to the same bucket. Through linear probing, they are stored consecutively in that bucket and the buckets below it.
![Distribution of key-value pairs in open addressing (linear probing) hash table](hash_collision.assets/hash_table_linear_probing.png){ class="animation-figure" }
@@ -1555,7 +1555,7 @@ The Figure 6-6 shows the distribution of key-value pairs in an open addressing
However, **linear probing tends to create "clustering"**. Specifically, the longer a continuous position in the array is occupied, the more likely these positions are to encounter hash collisions, further promoting the growth of these clusters and eventually leading to deterioration in the efficiency of operations.
It's important to note that **we cannot directly delete elements in an open addressing hash table**. Deleting an element creates an empty bucket `None` in the array. When searching for elements, if linear probing encounters this empty bucket, it will return, making the elements below this bucket inaccessible. The program may incorrectly assume these elements do not exist, as shown in the Figure 6-7 .
It's important to note that **we cannot directly delete elements in an open addressing hash table**. Deleting an element creates an empty bucket `None` in the array. When searching for elements, if linear probing encounters this empty bucket, it will return, making the elements below this bucket inaccessible. The program may incorrectly assume these elements do not exist, as shown in Figure 6-7.
![Query issues caused by deletion in open addressing](hash_collision.assets/hash_table_open_addressing_deletion.png){ class="animation-figure" }
+5 -5
View File
@@ -6,13 +6,13 @@ comments: true
A "hash table", also known as a "hash map", achieves efficient element querying by establishing a mapping between keys and values. Specifically, when we input a `key` into the hash table, we can retrieve the corresponding `value` in $O(1)$ time.
As shown in the Figure 6-1 , given $n$ students, each with two pieces of data: "name" and "student number". If we want to implement a query feature that returns the corresponding name when given a student number, we can use the hash table shown in the Figure 6-1 .
As shown in Figure 6-1, given $n$ students, each with two pieces of data: "name" and "student number". If we want to implement a query feature that returns the corresponding name when given a student number, we can use the hash table shown in Figure 6-1.
![Abstract representation of a hash table](hash_map.assets/hash_table_lookup.png){ class="animation-figure" }
<p align="center"> Figure 6-1 &nbsp; Abstract representation of a hash table </p>
Apart from hash tables, arrays and linked lists can also be used to implement querying functions. Their efficiency is compared in the Table 6-1 .
Apart from hash tables, arrays and linked lists can also be used to implement querying functions. Their efficiency is compared in Table 6-1.
- **Adding elements**: Simply add the element to the end of the array (or linked list), using $O(1)$ time.
- **Querying elements**: Since the array (or linked list) is unordered, it requires traversing all the elements, using $O(n)$ time.
@@ -513,7 +513,7 @@ index = hash(key) % capacity
Afterward, we can use `index` to access the corresponding bucket in the hash table and thereby retrieve the `value`.
Assuming array length `capacity = 100` and hash algorithm `hash(key) = key`, the hash function is `key % 100`. The Figure 6-2 uses `key` as the student number and `value` as the name to demonstrate the working principle of the hash function.
Assuming array length `capacity = 100` and hash algorithm `hash(key) = key`, the hash function is `key % 100`. Figure 6-2 uses `key` as the student number and `value` as the name to demonstrate the working principle of the hash function.
![Working principle of hash function](hash_map.assets/hash_function.png){ class="animation-figure" }
@@ -1824,7 +1824,7 @@ For the hash function in the above example, if the last two digits of the input
20336 % 100 = 36
```
As shown in the Figure 6-3 , both student numbers point to the same name, which is obviously incorrect. This situation where multiple inputs correspond to the same output is known as "hash collision".
As shown in Figure 6-3, both student numbers point to the same name, which is obviously incorrect. This situation where multiple inputs correspond to the same output is known as "hash collision".
![Example of hash collision](hash_map.assets/hash_collision.png){ class="animation-figure" }
@@ -1832,7 +1832,7 @@ As shown in the Figure 6-3 , both student numbers point to the same name, which
It is easy to understand that the larger the capacity $n$ of the hash table, the lower the probability of multiple keys being allocated to the same bucket, and the fewer the collisions. Therefore, **expanding the capacity of the hash table can reduce hash collisions**.
As shown in the Figure 6-4 , before expansion, key-value pairs `(136, A)` and `(236, D)` collided; after expansion, the collision is resolved.
As shown in Figure 6-4, before expansion, key-value pairs `(136, A)` and `(236, D)` collided; after expansion, the collision is resolved.
![Hash table expansion](hash_map.assets/hash_table_reshash.png){ class="animation-figure" }