需要说明的是,有序数组支持时间复杂度为O(1)的访问,所以可以使用二分查找,让查找速度达到O(logn)。
因为链表需要有序,所以在插入或删除时都要进行查找的操作,自然而然,它的时间复杂度变为了O(n)。
python中的dict,STL中的map。另外还提供了multimap,支持相同的关键词,被称为多重字典。
插入insert、删除erase,会自动按照字典序排列好。
另外还有专属map的下标操作。
find、count、lower_bound、upper_bound、equal_bound这些专属于关联容器的访问操作。
字典的另一种表示方法是散列。
概述可以参考散列表。
理想的散列的长度是固定的,就像预先知道了有多少球,只要按序号用哈希函数映射到对应的桶中即可。所以说其查找、插入、删除操作的时间都是 Θ ( 1 ) \Theta(1) Θ(1)。
但是由于关键字的变化范围很大,所以使得散列表没有意义或不切实际。
关键字范围太大,不能用理想方法表示时,可以采用不理想的散列表和散列函数。散列表位置的数量比关键字个数少时,散列函数把若干个不同的关键字映射到散列表的同一位置。
此时,散列表的每个位置叫一个桶。桶的数量等于散列表的长度或大小。
在多种散列函数中,最常见的是除法散列函数: f ( k ) = k % D f(k)=k\%D f(k)=k%D
k是关键字,D是散列表的长度。
严格来说散列函数分为散列码和压缩函数两部分,哈希码就是把一些符号转化为一个值,压缩函数实现值与桶号的映射。
当两个不同的关键字所对应的起始桶相同时,冲突发生。
如果存储桶没有空间存储一个新数对,就是溢出发生。
如果每个桶只能存储一个数对,那么碰撞和溢出会同时发生。
冲突并不可怕,可怕的是溢出。当映射到散列表中任何一个桶里的关键字数量大致相等时,冲突和溢出的平均数最少。均匀散列函数便是这样的函数。在实际应用中表现良好的均匀散列函数又被称为良好散列函数。
C11采用unordered_map代替了原来的hash_map,其底层实现正是散列表。
一些功能参考无序容器。
关于unordered_map和map的区别可以参考这个。
C++STL中的压缩函数是值模上桶数。我们需要该写的是哈希码。
如果桶f(k)已经被填满,那么顺序找下一个可用的桶,这一方法被称为线性探查。
在寻找下一个可用的桶时,散列表被视为环形表。
明白了怎么用线性探查法插入,就可以设计散列表的搜索方法。假设要查找关键字为k 的数对,首先搜索起始桶f(k),然后把散列表当做环表继续搜索下一个桶,直到以下情况之一发生为止:
后面两种情况表示桶并不存在。
删除一个桶时,不能仅仅把桶置空,这样会影响继续搜索下一个桶(删除后就是一个空桶,阻断了之后的数的查找)。合理的做法是从删除位置的下一个位置开始,逐个检查每个桶,直到到达一个空桶或者回到要删除的位置。
常见的做法是为每个桶增加一个域neverUsed,起始时被设置为true,一旦被填入数对,就变成false,再也不改变(即使数对被删除,这是避免影响后续搜索或者移动)。当很多空桶的neverUsed变为false时,需要重新组织这个散列表。比如,可以把余下的数对都插到一个空的散列表中。
设b为散列表的桶数,D为散列函数的除数,且b=D。散列表初始化的时间为O(b)。
当表中有n个记录时,最坏情况下插入和查找的时间均为 Θ ( n ) \Theta(n) Θ(n),这是出现在所有关键字都对应同一个起始桶。
令 U n U_n Un和 S n S_n Sn分别表示在一次成功搜索和不成功搜索中平均搜索的桶数,对于线性探查有以下近似公式:
U n = 1 2 ( 1 + 1 ( 1 − α ) 2 ) U_n=\frac{1}{2}(1+\frac{1}{(1-\alpha)^2}) Un=21(1+(1−α)21)
S n = 1 2 ( 1 + 1 1 − α ) S_n=\frac{1}{2}(1+\frac{1}{1-\alpha}) Sn=21(1+1−α1)
其中 α = n b \alpha=\frac{n}{b} α=bn为负载因子。
随着负载因子的增加,所需查找的桶的数量也随之增大,一般要求 α ≤ 0.75 \alpha\le0.75 α≤0.75。
rehash(n)
,reverse(n)
,c.max_load_factor
都是维护负载因子的手段。
线性探测法属于开放寻址法中的一种,除此之外还有二次探测这种方法。
该结构是解决溢出/冲突的另一种方法,又称为分离链表法。即给散列表的每一个位置配置一个线性表。
这也是C++STL中unordered_map的实现解决冲突所采用的方法,这是一种以空间换取时间的手段。
U n = α ( α + 3 ) 2 ( α + 1 ) U_n=\frac{\alpha(\alpha+3)}{2(\alpha+1)} Un=2(α+1)α(α+3)
S n = 1 + α 2 S_n=1+\frac{\alpha}{2} Sn=1+2α