哈希函数与哈希表
哈希函数和哈希表的实现
哈希函数
哈希函数(Hash function)是一种将任意长度的消息映射到固定长度的输出的函数。它接收一个消息或数据块作为输入,然后使用算法将其转换为固定长度的哈希值或消息摘要,通常是一个定长的字符串或数字。哈希函数的主要作用是保证数据的完整性和一致性。
常用的哈希函数有MD5(生成的哈希值范围 [ 0 , 2 64 − 1 ] [0,2^{64}-1] [0,264−1])、SHA-1(生成的哈希值范围 [ 0 , 2 128 − 1 ] [0,2^{128}-1] [0,2128−1])、SHA-256等。
哈希函数特点
- 固定输出长度:哈希函数将任意长度的输入消息映射为固定长度的哈希值,这个长度通常是固定的,不会因输入的大小而改变。
- 确定性:对于相同的输入消息,哈希函数总是会生成相同的哈希值。这种特性可以用来验证数据的完整性和一致性。
- 不可逆性:从哈希值反推出原始消息是困难的甚至不可能的。因此,哈希函数是一种单向函数,它可以确保原始数据的保密性。
- 抗碰撞性:对于不同的输入消息,哈希函数生成的哈希值应该是不同的。尽管在理论上可能存在两个不同的消息生成相同的哈希值(称为哈希碰撞),但是好的哈希函数应该能够最大限度地减少碰撞的概率。
- 散列性:哈希函数应该能够将输入消息的每一个比特位都均匀地分散到哈希值中,以最大限度地减少哈希碰撞的可能性。
哈希表
哈希表(Hash Table)是一种基于哈希函数实现的数据结构,用于高效地存储和查找数据。其底层实现通常包括以下几个部分:
- 数组:哈希表通常是基于数组实现的。数组的每个元素都可以存储一个数据项,数据项包括键值对或者其他需要存储的信息。
- 哈希函数:哈希函数将输入的关键字映射为一个数组下标。这个函数应该能够将不同的输入映射到不同的下标,并尽可能地减少哈希碰撞的概率。常用的哈希函数包括除余法、乘法哈希等。
- 冲突处理:由于哈希函数可能存在哈希碰撞,即将不同的关键字映射到同一个下标的情况。为了解决这个问题,哈希表通常采用开放地址法或者链地址法来处理哈希碰撞。
- 链地址法:当出现哈希碰撞时,将数据插入到一个链表中。如果多个关键字映射到同一个下标,那么它们会存储在同一个链表中。
- 开放地址法:当出现哈希碰撞时,从当前下标开始依次查找空闲位置,并将数据插入到第一个空闲位置。常用的开放地址法包括线性探测、二次探测、双重散列等。 - 扩容:随着数据的不断插入,哈希表的负载因子(元素个数与数组大小的比值)会增大,导致哈希表的性能下降。为了解决这个问题,哈希表通常会设置一个负载因子阈值,当负载因子超过阈值时,就需要进行扩容操作,即创建一个更大的数组,将所有数据重新哈希到新数组中。假设一共N个元素,单次扩容代价为 O ( N ) O(N) O(N),扩容次数为 O ( l o g N ) O(log N) O(logN)(实际上因为负载因子阈值的存在, l o g N logN logN前的常数很小,使用时可以认为接近O(1)),所以总扩容代价为 O ( N ∗ l o g N ) O(N*logN) O(N∗logN)。
设计类型例题
设计RandomPool结构
题目说明
设计一种结构,在该结构中有如下三个功能:
insert(key):将某个key加入到该结构,做到不重复加入;
delete(key):将原本在结构中的某个key移除;
getRandom():等概率随即返回结构中的任何一个key。
要求三种方法的时间复杂度都是O(1)。
设计思路
分别用两个哈希表Map1<K, Integer>,Map2<Integer, K>来存储数据,Map1的值和Map2的键为所给Key对应的index(注意,这里的索引和数组没有关系,存粹是为了等概率返回Key的方法设计),再用一个整型变量size记录当前的key总数。insert(key)时将(key, size)插入Map1,(size, key)插入Map2,然后size++;getRandom()时直接获取一个[0,size]的随机数然后返回Map2中对应的值即可;关键是delete(key)的操作,要让随机数保证能选到有效的index,我们可以将Map1中最后(这里的最后相对index而言,因为哈希表内部实际上是无序的)一个键值对的值更新为要删除的那个key的index(Map2同理也要更新),然后size--,要删除的key从两个Map中删除,也即用最后一个key去填补删掉的那个空,这样就可以保证随机数选中有效的位置,同时概率不变。
代码实现
public class RandomizedSet<K> {
HashMap<K, Integer> valToIndex

最低0.47元/天 解锁文章

被折叠的 条评论
为什么被折叠?



