diff --git a/notes/19_hashtable/readme.md b/notes/19_hashtable/readme.md index d530c05..7f985bf 100644 --- a/notes/19_hashtable/readme.md +++ b/notes/19_hashtable/readme.md @@ -1,59 +1,59 @@ -# 散列表 - -核心:散列表的效率并不总是 $O(1)$,仅仅是在理论上能达到 $O(1)$。实际情况中,恶意攻击者可以通过精心构造数据,使得散列表的性能急剧下降。 - -如何设计一个工业级的散列表? - -## 散列函数 - -* 不能过于复杂——避免散列过程耗时 -* 散列函数的结果要尽可能均匀——最小化散列冲突 - -## 装载因子过大怎么办 - -动态扩容。涉及到 rehash,效率可能很低。 - -![](https://static001.geekbang.org/resource/image/67/43/67d12e07a7d673a9c1d14354ad029443.jpg) - -如何避免低效扩容? - -——将 rehash 的步骤,均摊到每一次插入中去: - -* 申请新的空间 -* 不立即使用 -* 每次来了新的数据,往新表插入数据 -* 同时,取出旧表的一个数据,插入新表 - -![](https://static001.geekbang.org/resource/image/6d/cb/6d6736f986ec4b75dabc5472965fb9cb.jpg) - -## 解决冲突 - -开放寻址法,优点: - -* 不需要额外空间 -* 有效利用 CPU 缓存 -* 方便序列化 - -开放寻址法,缺点: - -* 查找、删除数据时,涉及到 `delete` 标志,相对麻烦 -* 冲突的代价更高 -* 对装载因子敏感 - -链表法,优点: - -* 内存利用率较高——链表的优点 -* 对装载因子不敏感 - -链表法,缺点: - -* 需要额外的空间(保存指针) -* 对 CPU 缓存不友好 - -——将链表改造成更高效的数据结构,例如跳表、红黑树 - -## 举个栗子(JAVA 中的 HashMap) - -* 初始大小:16 -* 装载因子:超过 0.75 时动态扩容 -* 散列冲突:优化版的链表法(当槽位冲突元素超过 8 时使用红黑树,否则使用链表) +# 散列表 + +核心:散列表的效率并不总是 $O(1)$,仅仅是在理论上能达到 $O(1)$。实际情况中,恶意攻击者可以通过精心构造数据,使得散列表的性能急剧下降。 + +如何设计一个工业级的散列表? + +## 散列函数 + +* 不能过于复杂——避免散列过程耗时 +* 散列函数的结果要尽可能均匀——最小化散列冲突 + +## 装载因子过大怎么办 + +动态扩容。涉及到 rehash,效率可能很低。 + +![](https://static001.geekbang.org/resource/image/67/43/67d12e07a7d673a9c1d14354ad029443.jpg) + +如何避免低效扩容? + +——将 rehash 的步骤,均摊到每一次插入中去: + +* 申请新的空间 +* 不立即使用 +* 每次来了新的数据,往新表插入数据 +* 同时,取出旧表的一个数据,插入新表 + +![](https://static001.geekbang.org/resource/image/6d/cb/6d6736f986ec4b75dabc5472965fb9cb.jpg) + +## 解决冲突 + +开放寻址法,优点: + +* 不需要额外空间 +* 有效利用 CPU 缓存 +* 方便序列化 + +开放寻址法,缺点: + +* 查找、删除数据时,涉及到 `delete` 标志,相对麻烦 +* 冲突的代价更高 +* 对装载因子敏感 + +链表法,优点: + +* 内存利用率较高——链表的优点 +* 对装载因子不敏感 + +链表法,缺点: + +* 需要额外的空间(保存指针) +* 对 CPU 缓存不友好 + +——将链表改造成更高效的数据结构,例如跳表、红黑树 + +## 举个栗子(JAVA 中的 HashMap) + +* 初始大小:16 +* 装载因子:超过 0.75 时动态扩容 +* 散列冲突:优化版的链表法(当槽位冲突元素超过 8 时使用红黑树,否则使用链表)