数据结构与算法系列二十(初识散列表)

122 阅读3分钟

如果你是一个java程序员,在日常开发中,有一个集合你一定经常使用,它是:HashMap

如果你是一个有经验的java程序员,在日常开发中,你一定知道如何使用HashMap,性能会更好。比如说,你会从这么几个方面考虑:

  • 设置初始大小

    • HashMap默认的初始容量是16,在实际项目中,我们可以考虑预估要存入的元素个数,根据元素个数设置合适的初始容量。减少HashMap动态扩容,减少重建哈希表,从而提升性能
/**
* The default initial capacity - MUST be a power of two.
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

装载因子和动态扩容

  • HashMap装载因子,默认是0.75。表示在HashMap中,当元素的个数超过:capacity * 0.75的时候,就会启动动态扩容,每次扩容后容量大小都是之前的两倍
  • 装载因子越大,表示空闲空间越小,对应的HashMap冲突的概率就会越大。在实际项目中,我们可以设置合适的装载因子,提升HashMap性能
/**
* The load factor used when none specified in constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;

散列冲突解决方法

  • HashMap的散列冲突解决方法是:拉链法(即链表)。在jdk1.8中,如果某个链表的元素超过8个,会转换成红黑树。因此在jdk1.8中,HashMap的散列冲突方法是:拉链法+红黑树

image.png

如果你是一个有追求的java程序员,那么你一定还想知道,HashMap底层使用了什么样的数据结构,它就是我们今天的主角:散列表

案例

定义散列表

关于什么是散列表,我们从一个故事开始:

1.今天是五四青年节,假设你还是一个大学生

2.你们班上学生比较多,是一个大班级,假设有100人,学生编号从0-100

3.你们学校,或者说你们班上准备组织一个有奖比赛活动

4.活动结束后,会统计每个学生的比赛成绩,根据成绩进行排名颁奖

5.由于你平时喜欢写程序,于是你很荣幸,辅导员就这次比赛活动邀请你开发一个程序,负责统计每个比赛选手的成绩

6.问题:

  • 你该怎么做,才能实现根据编号,快速查找到比赛选手的比赛成绩?
  • 比如给定编号50,在时间复杂度O(1)下,得到编号50选手的成绩。
#分析:
1.聪明的你,肯定已经找到了关键词:查找操作,时间复杂度O(1)
2.好熟悉,有没有?这不就是数组吗?

#散列表定义:
1.散列表数据结构,是数组的一种扩展,利用了数组支持按照下标随机访问特性(O(1)时间复杂度)
2.比如上面故事的解决办法:
  2.1.设计一个散列函数:hash(key)=key,这里的key是学生编号:从0到100
  2.2.将hash(key)的值,作为数组的下标,对应的元素值是:选手的比赛成绩
  2.3.于是满足:给定任意编号50,把hash(50)=50作为数组下标,按照下标随机访问数组,在O(1)时间复杂度下,得到对应选手的比赛成绩。如下图:

image.png

散列表关键知识点

散列表的关键知识点有:

  • 散列函数

    • 散列函数hash(key),用于计算key与数组下标的对应关系
    • 如何设计一个好的散列函数,我们将在下一篇中分享
  • 散列冲突解决方法 通过散列函数hash(key),计算出的散列值。在实际应用中,不同的key,经过hash(key),如果散列值相同,我们称为散列冲突。散列冲突的解决方法有:

    • 开放寻址法
    • 拉链法
    • 关于开放寻址法,与拉链法,我们将在下一篇中分享