当存在固定大小的数据时,合并哈希是一种避免冲突的技术。它是单独链接和开放寻址的结合。它使用开放寻址(线性探测)的概念从哈希表的底部查找冲突元素的第一个空白位置,并使用单独链接的概念通过指针将冲突元素彼此链接。使用的哈希函数为h =(key)%(键总数) 。在哈希表中,每个节点具有三个字段:
- h(key):密钥的哈希函数的值。
- 数据:密钥本身。
- 下一个:指向下一个碰撞元素的链接。
合并哈希的基本操作是:
- INSERT(key):如果表中的哈希值为空,则插入操作将根据该键的哈希值插入密钥,否则,将密钥插入哈希表底部的第一个空位置以及此空地址位置映射到链中上一个指向节点的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为空,因此在6索引值处插入了16。
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索引值。这个新插入的节点的地址(从地址开始,我们的意思是在第0个索引值节点的下一个字段中初始化节点的索引值(即(9))。
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个索引节点的下一个字段中插入新节点的地址(即(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个索引值处插入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,因此将其插入第三个索引值,并将该节点的地址映射到第二个索引值节点的下一个字段中。
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个索引,其中在下一个包含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:如果要删除的密钥是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 |