5.3冲突
前面说过,大多数语言都提供了散列表实现,你不用知道如何实现它们。有鉴于此,我就不再过多地讨论散列表的内部原理,但你依然需要考虑性能!要明白散列表的性能,你得先搞清楚什么是冲突。本节和下一节将分别介绍冲突和性能。
首先,我撒了一个善意的谎。我之前告诉你的是,散列函数总是将不同的键映射到数组的不同位置。
不好,这个位置已经存储了苹果的价格!怎么办?这种情况被称为冲突(collision):给两个键分配的位置相同。这是个问题。如果你将鳄梨的价格存储到这个位置,将覆盖苹果的价格,以后再查询苹果的价格时,得到的将是鳄梨的价格!冲突很糟糕,必须要避免。处理冲突的方式很多,最简单的办法如下:如果两个键映射到了同一个位置,就在这个位置存储一个链表。
这里的经验教训有两个。
- 散列函数很重要。前面的散列函数将所有的键都映射到一个位置,而最理想的情况是,散列函数将键均匀地映射到散列表的不同位置。
- 如果散列表存储的链表很长,散列表的速度将急剧下降。然而,如果使用的散列函数很好,这些链表就不会很长!
散列函数很重要,好的散列函数很少导致冲突。那么,如何选择好的散列函数呢?这将在下一节介绍!