开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。
简单的来说:表里面存的是链表的头指针,链表里存的是冲突数据。
从上图可以看出,开散列中每个桶中放的都是发生哈希冲突的元素。
大致结构如下:
Node的定义我们可以给一个构造函数:
那么我们该怎么插入呢?
还是和线性探测差不多,先映射到对应的位置:
然后,我们要进行头插,因为单链表的头插效率比较高:
先创建一个新的结点,将新结点先指向表里的头指针,然后表的指针再指向新结点。
然后,我们还需要写扩容:
这里是负载因子为1的时候才去扩容。和线性探测的方式差不多。当表里的结点不是空指针时,我们就遍历这个链表,一个一个插入进去。最后进行交换。
其实我们可以改进这个扩容:
这个思想是将原来结点转移到新的顺序表里,这样我们就不需要再重新创建新结点了。因为原来表里还指向结点,所以我们把它置空。
当我们把新表和老表交换之后,出作用域,我们需要将老表销毁,vector会调用自己的析构函数销毁,但是里面挂着的链表不能被自动销毁,所以我们要自己写一下:
思路很简单,先找到对应的位置,如果不为空,就遍历链表。
这里的删除就类似单链表的删除。
哈希桶不需要自己写构造函数,因为vector会自动生成。但是我们需要写拷贝构造和赋值函数。
但是,我们写了拷贝构造函数,那么编译器就不会默认生成构造函数了。所以我们用到C++11的一个特性:
这样编译器会默认生成构造函数,而拷贝构造还是我们自己写的。
最后,我们需要传仿函数让其它类型也能变成整数就完成了。