这是我参与2022首次更文挑战的第5天,活动详情查看:2022首次更文挑战。
实现快速查找的数据结构:散列表
散列表是一种通过将键(key)映射为值(value)从而实现快速查找的数据结构。实现散列表的方法有很多种。 我们使用一个链表构成的数组与一个散列函数来实现散列表。当插入键(字符串或几乎其他所有数据类型)和值时,我们按照如下方法操作。
(1) 首先,计算键的散列值。键的散列值通常为int或者long型。请注意,不同的两个键可以有相同的散列值,因为键的数量是无穷的,而int型的总数是有限的。
(2) 之后,将散列值映射为数组的索引。可以使用类似于hash(key) % array_length的方式完成这一步骤,不同的两个散列值则会被映射到相同的数组索引。
- 不同的两个散列值则会被映射到相同的数组索引,我们称之为哈希冲突。解决哈希冲突也有几种方法。线性探测法:拉链法、开放定址法(这种方法也称再散列法)、 再哈希法(这种方法是同时构造多个不同的哈希函数)、建立公共溢出区。
(3) 此数组索引处存储的元素是一系列由键和值为元素组成的链表。请将映射到此索引的键和值存储在这里。由于存在冲突,我们必须使用链表:有可能对于相同的散列值有不同的键,也有可能不同的散列值被映射到了同一个索引。
通过键来获取值则需重复此过程。首先通过键计算散列值,再通过散列值计算索引。之后,查找链表来获取该键所对应的值。
如果冲突发生很多次,最坏情况下的时间复杂度是O(N),其中N是键的数量。但是,我们通常假设一个不错的实现方式会将冲突数量保持在最低水平,在此情况下,时间复杂度是。
另一种方法是通过平衡二叉搜索树来实现散列表。该方法的查找时间是O(log N)。该方法的好处是用到的空间可能更少,因为我们不再需要分配一个大数组。还可以按照键的顺序进行迭代访问,在某些时候这样做很有用。
高效的软件离不开散列表,因为其O(1)的读取和插入带来了无与伦比的性能优势。