极客时间课程《数据结构与算法之美》笔记06 - 散列表

散列表(Word文档中的单词拼写检查功能)

优点

  • 模拟映射关系
  • 防止重复
  • 缓存、记住数据,以避免服务器再经过处理生成。
  • 查找、插入、删除都很是快。
  • 能够结合散列函数和数组来建立散列表,通常编程语言都提供了实现。

散列表执行各类操做的时间都为O(1),常量时间,不管散列表多大,所需时间都相同。
平均状况下,查找与数组同样快,插入和删除速度与链表同样快。
填装因子: = 散列表占用位置 / 位置总数
填装因子越大,说明空闲位置越少,冲突越多,性能会降低。
一个小的经验规则:一旦填装因子大于0.7,就调整散列表的长度。
一个好的散列函数:SHA。java

思想

数组支持按照下标随机访问数据的特性,散列表是数组的一种扩展,由数组演化而来。编程

散列冲突

  • 开放寻址法
    出现了冲突就从新探测一个空闲位置插入。
    好比线性检测
    查找的过程当中,遍历到空闲位置结束。
    删除的时候通常不是置空,而是标记为delete,由于置空会影响线性探测。
    二次探测:二次探测和线性探测相比,探测的步长为2。
    双重散列:不单单使用一个散列函数,若是第一个散列函数冲突,就使用第二个散列函数,依次类推。api

  • 链表法
    每一个元素位置再接一个链表。那个元素位置叫作桶或槽。多增长了链表的遍历。
    时间复杂度和链表的长度成正比。散列比较均匀的话,k = n/m,n = 数据个数,m是槽的个数。数组

词典检查就是把全部单词存储在散列表中进行查找,验证是否能找到。空间不大,放在内存里就行。缓存

工业级散列表设计

动态扩容,均摊扩容(插入到新的散列表中,同时把老的散列表中一个数据拿到新的里面。)服务器

当数据量比较小、装载因子小的时候,适合采用开放寻址法。这也是Java 中的 ThreadLocalMap 使用开放寻址法的缘由。数据结构

虽然链表法比较耗费空间,可是若是存储的是大对象,那么链表中的结点占用空间不多,因此能够忽略不计。
比较适合存储大对象,大数据量的散列表,能够用红黑树代替链表。编程语言

HashMap 分析

默认是初始大小是16,能够调节。
最大装载因子是0.75,启动扩容会扩大到两倍大小。函数

底层使用链表法解决冲突,JDK1.8版本中,链表长度超过8时,链表转化为红黑树。当红黑树结点少于8个的时候,红黑树转化为链表。性能

如下为其散列函数。

int hash(Object key) {
    int h = key.hashCode();//返回Java对象的hash code
    return (h ^ (h >>> 16)) & (capitity -1); //capicity 表示散列表的大小
}

散列表和链表的结合

添加、删除、查找操做时间复杂度降到O(1)。
散列表中嵌入双向链表,双向链表增长特殊字段hnext。
结点除了可使用前驱后继指针访问以外,还能够经过hnext索引访问(拉链)。
疑惑点:为什么查找到一个结点以后将其放在链表尾部。

Redis有序集合
Redis和跳表关系很紧密

Java LinkedHashMap
LinkedHashMap能够支持按照插入顺序遍历数据,还支持按照访问顺序来遍历数据。
Linked指的是双向链表。
访问完数据以后也会放到链表结尾,估计是LRU策略(最近最少使用)

小结

散列表没法按照某种顺序快速遍历,一个方法是拷贝到数组中,排序遍历。 散列表是动态数据结构,不停有插入、删除状况。若是按照顺序遍历散列表,那么经常会将散列表和链表(或者跳表)一块使用。

相关文章
相关标签/搜索