当存在固定大小的数据时,合并散列是一种避免冲突的技术。它是单独链接和开放寻址的组合。它使用开放寻址(线性探测)的概念从哈希表的底部找到第一个冲突元素的空位,并使用分离链的概念通过指针将冲突元素相互链接。使用的哈希函数是h=(key)%(total number of keys) 。在哈希表中,每个节点都有三个字段:
- h(key):一个键的哈希函数值。
- 数据:密钥本身。
- Next:指向下一个碰撞元素的链接。
Coalesced hashing 的基本操作是:
- INSERT(key):如果表中的哈希值为空,则插入操作根据该键的哈希值插入键,否则将键插入哈希表底部的第一个空位置和该空的地址place 映射到链的前一个指向节点的 NEXT 字段中。(在下面的示例中解释)。
- DELETE(Key):如果存在的键被删除。此外,如果要删除的节点在哈希表中包含另一个节点的地址,则该地址将映射到指向要删除的节点的节点的 NEXT 字段中
- SEARCH(key):如果key存在则返回True ,否则返回False。
所有这些操作的最佳情况复杂度为 O(1),最坏情况复杂度为 O(n),其中 n 是键的总数。它比单独链接更好,因为它将冲突元素插入到哈希表的内存中而不是像在单独的链接中那样创建一个新的链表。
插图:
示例:
n = 10
Input : {20, 35, 16, 40, 45, 25, 32, 37, 22, 55}
哈希函数
h(key) = key%10
脚步:
- 最初创建空哈希表,所有 NEXT 字段初始化为 NULL 和 h(key) 值范围为 0-9。
Hash value Data Next 0 – NULL 1 – NULL 2 – NULL 3 – NULL 4 – NULL 5 – NULL 6 – NULL 7 – NULL 8 – NULL 9 – NULL - 让我们从插入 20 开始,因为 h(20)=0 并且 0 索引为空,所以我们在 0 索引处插入 20。
Hash value Data Next 0 20 NULL 1 – NULL 2 – NULL 3 – NULL 4 – NULL 5 – NULL 6 – NULL 7 – NULL 8 – NULL 9 – NULL - 下一个要插入的元素是 35,h(35)=5 并且第 5 个索引为空,所以我们在那里插入 35。
Hash value Data Next 0 20 NULL 1 – NULL 2 – NULL 3 – NULL 4 – NULL 5 35 NULL 6 – NULL 7 – NULL 8 – NULL 9 – NULL - 接下来我们有 16,h(16)=6 是空的,所以 16 被插入到 6 索引值。
Hash value Data Next 0 20 NULL 1 – NULL 2 – NULL 3 – NULL 4 – NULL 5 35 NULL 6 16 NULL 7 – NULL 8 – NULL 9 – NULL - 现在我们必须插入 40, h(40)=0 已经被占用了,所以我们从底部搜索第一个空块并将其插入那里,即 9 索引值。还有这个新插入节点的地址(从地址我们的意思是节点的索引值)即(9)在第 0 个索引值节点的下一个字段中初始化。
Hash value Data Next 0 20 9 1 – NULL 2 – NULL 3 – NULL 4 – NULL 5 35 NULL 6 16 NULL 7 – NULL 8 – NULL 9 40 NULL - 要插入 45,h(45)=5 被占用,所以我们再次从底部搜索空块即 8 索引值并将这个新插入的节点 ie(8) 的地址映射到第 5 个索引值节点的 Next 字段即在 key=35 的下一个字段中。
Hash value Data Next 0 20 9 1 – NULL 2 – NULL 3 – NULL 4 – NULL 5 35 8 6 16 NULL 7 – NULL 8 45 NULL 9 40 NULL - 在插入 25 旁边,h(25)=5 被占用,因此从底部搜索第一个空块,即第 7 个索引值并在那里插入 25。现在需要注意的是,这个新节点的地址不能映射到已经指向其他节点的第 5 个索引值节点。要插入新节点的地址,我们必须遵循从第 5 个索引节点开始的链接链,直到我们在下一个字段中获得 NULL 并将新节点的地址映射到该节点的下一个字段,即从第 5 个索引节点我们转到第 8 个索引节点在下一个字段中包含 NULL,因此我们在第 8 个索引节点的下一个字段中插入新节点 ie(7) 的地址。
Hash value Data Next 0 20 9 1 – NULL 2 – NULL 3 – NULL 4 – NULL 5 35 8 6 16 NULL 7 25 NULL 8 45 7 9 40 NULL - 要插入 32,h(32)=2,这是空的,所以在第二个索引值处插入 32。
Hash value Data Next 0 20 9 1 – NULL 2 32 NULL 3 – NULL 4 – NULL 5 35 8 6 16 NULL 7 25 NULL 8 45 7 9 40 NULL - 要插入 37,h(37)=7 已被占用,因此从底部搜索第一个空闲块,即第 4 个索引值。因此在第 4 个索引值处插入 37 并将该节点的地址复制到第 7 个索引值节点的下一个字段中。
Hash value Data Next 0 20 9 1 – NULL 2 32 NULL 3 – NULL 4 37 NULL 5 35 8 6 16 NULL 7 25 4 8 45 7 9 40 NULL - 要插入 22,h(22)=2 被占用,因此将其插入到第 3 个索引值处,并将该节点的地址映射到第 2 个索引值节点的下一个字段中。
Hash value Data Next 0 20 9 1 – NULL 2 32 3 3 22 NULL 4 37 NULL 5 35 8 6 16 NULL 7 25 4 8 45 7 9 40 NULL - 最后,插入 55 h(55)=5 被占用,唯一的空白空间是第一个索引值,所以在那里插入 55。现在再次映射这个新节点的地址,我们必须遵循从第 5 个索引值节点开始的链,直到我们在下一个字段中获得 NULL,即从第 5 个索引->第 8 个索引->第 7 个索引->第 4 个索引,其中在 Next 中包含 NULL字段,我们在第 4 个索引值节点插入新插入节点的地址。
Hash value | Data | Next |
---|---|---|
0 | 20 | 9 |
1 | 55 | NULL |
2 | 32 | 3 |
3 | 22 | NULL |
4 | 37 | 1 |
5 | 35 | 8 |
6 | 16 | NULL |
7 | 25 | 4 |
8 | 45 | 7 |
9 | 40 | NULL |
删除过程很简单,例如:
情况 1:要删除 key=37,首先搜索 37。如果它存在,则只需删除数据值,并且如果该节点在下一个字段中包含任何地址,并且要删除的节点即 37 本身由某个其他节点指向(即key=25)然后将37的下一个字段中的地址复制到指向37的节点的下一个字段(即key=25)并再次将key=37的NEXT字段初始化为NULL并擦除key=37。
Hash value | Data | Next |
---|---|---|
0 | 20 | 9 |
1 | 55 | NULL |
2 | 32 | 3 |
3 | 22 | NULL |
4 | – | NULL |
5 | 35 | 8 |
6 | 16 | NULL |
7 | 25 | 1 |
8 | 45 | 7 |
9 | 40 | NULL |
情况 2:如果要删除的 key 是 35,并且没有被任何其他节点指向,那么我们必须拉动附加到要删除的节点的链,即 35 后退一步,并将链的最后一个值再次标记为 NULL。
Hash value | Data | Next |
---|---|---|
0 | 20 | 9 |
1 | – | NULL |
2 | 32 | 3 |
3 | 22 | NULL |
4 | – | NULL |
5 | 45 | 8 |
6 | 16 | NULL |
7 | 55 | NULL |
8 | 25 | 7 |
9 | 40 | NULL |
如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程和学生竞争性编程现场课程。