UTF8gbsn
介绍
本节课程的主要内容在于hash的增长以及缩减。还介绍了一个查找子串的方法。
增长
假设我们的key现在有n个。而hash的抽屉的个数为m。那么有两种增长的策略。
-
如果 n ⩾ m n\geqslant m n⩾m,那么我们将hash表的抽屉数量m加1.然后对前面已经插入的所有元素进行一次重新hash。那么这个算法的时间复杂度为 Θ ( n 2 ) \Theta(n^2) Θ(n2)。因为你插入n个元素需要进行重新hash次数为
Θ ( 1 + 2 + ⋯ + n ) = Θ ( n 2 ) \Theta(1+2+\cdots+n)=\Theta(n^2) Θ(1+2+⋯+n)=Θ(n2) -
当 n ⩾ m n\geqslant m n⩾m的时候我们将m增加到2m.这种策略的时间复杂度为 Θ ( n ) \Theta(n) Θ(n) .比如你要插入n个元素.那么需要重新hash的次数为
Θ ( 1 + 2 + 4 + ⋯ + n ) = Θ ( n ) \Theta(1+2+4+\cdots+n)=\Theta(n) Θ(1+2+4+⋯+n)=Θ(n)
缩减
如果你的hash表进行了删除,那么为防止过分的资源浪费我们需要释放掉一部分的资源.那么策略是什么呢?
这里主要采用的策略是如果你的
n
⩽
1
4
m
n\leqslant \frac{1}{4}m
n⩽41m,
那么
m
m
m就进行减半,编程
m
/
2
m/2
m/2.
为什么不在
n
⩽
1
2
m
n\leqslant \frac{1}{2}m
n⩽21m的地方进行减半呢?主要是为防止在临界点频繁增删,这样会导致数据结构频繁释放资源和分配资源.
example
python 中的list就是按照上面的方式进行的增减操作.
Karp-Rabin Algorithm
这个算法是一个查找子串的算法,首先假设目标串为s,我们希望在t串中找寻s串.这个算法利用hash的方法来做.
-
将s进行hash.
-
然后按照s的长度在t上进行滑动,每滑动一次可以得到一个子串.
-
如果得到的子串的hash值和s的hash值相同.我们在进行真正的比较.
这个算法的期望时间复杂度为 O ( ∣ s ∣ + ∣ t ∣ c o s t ( h ) ) O(|s|+|t|cost(h)) O(∣s∣+∣t∣cost(h)),这与传统的子串比较复杂度 O ( ∣ s ∣ ⋅ ∣ t ∣ ) O(|s|\cdot|t|) O(∣s∣⋅∣t∣)比起来,有明显的优势.另外应该注意的是,我们还有一个算法KMP算法.它的最坏时间复杂度为 O ( n ) O(n) O(n)
Rolling Hash ADT
这个算法可以用来加速 Karp-Rabin Algorithm.它的定义如下
-
r(), 在x上进行hash的函数
-
r.append©,将c附加到x的后面.
-
r.skip(),将最前面的字符删除.
这个算法省去了求中间重部分hash的过程,可以进一步提高算法的速度.