哈希表应用

哈希表-查询O(1)复杂度的利器。

理论上说,当输入规模够大时是不可能没有冲突的,因为两个域要有映射,且结果域是远远小于输入域的,选取一个好的哈希函数固然重要,但是解决冲突的方法也是必不可少。

这里先普及一些东西:哈希函数,在随机大样本测试下,基于概率学的研究,每个桶内部的元素个数是差不多的。

为了避免冲突,我们有一些神奇的方法:

1)以int-4字节数据为例,将数据转换为二进制,奇数位与偶数位分离,各自放在新的32位整形的低16位上,再异或。

2)选取2的次幂的位置,与剩下的二进制进行异或。

3)对素数取模。

总之,只要操作方式足够随机,或是哪怕不随机,但是选取的元素间毫无关系可言【例如二进制下不同的位】,就可以称为一个好的哈希函数。

解决冲突的方式:

1.开放定址   哈希到冲突位置后,给定可变或不变步长,向后寻找空位置。

2.链表法  在桶后面接上链表

优化:据说JVM里面的hash,是在桶后面建红黑树

3.再hash  造出足够多的hash函数,冲突则将hash(i)函数的结果用hash(i+1)函数再计算。

如何造出那么多hash:多hash的前提是,hash不能与任意一个hash有相关性。

现假设我们有两个非相关hash,hash1和hash2,我们可以用加权和的方式造出无穷个hash:

hash3=hash1+hash2,hash4=hash1+2*hash2,也就是hash(i)=i*hash1+hash2,hash3虽然看起来和hash1和hash2有关,但是由于hash1、hash2不相关,因此他们加权和的结果和各自都不存在相关性。

 

要求设计一种结构,增删改查都可以做到O(1),并且还可以等概率返回其中的一个元素。

查询要O(1)?那毫无疑问就是hash了,问题是hash如何做到等概率返回一个元素呢?

遍历:O(n)。

遍历的目的是什么:我们需要知道有多少个元素。

遍历如何实现等概率返回:遍历的同时将数据打到一个数组A里,然后用随机函数rand()生成一个1~n【注:由于这个结构支持增删,所以n是可变的】的index,然后返回A[index]。

避免遍历:如果我们一开始就给这些元素index,会怎么样?

我们用一个hash来存储index->value对,同时用size记录下当前的最大索引,这样rand()生成1~size之间的数的时候就不需要遍历就可以知道长度了,同时,复杂度满足O(1)的要求。

 

那么删除呢:我们的index是从1开始按照步长为1递增的。我们很有可能把不是结尾元素的第i号元素删除了,那么这个时候i对应的value就会空掉,此时必须经过多次rand(),一旦删除元素过多,由于index是不断递增的,碰到空的几率会越来越大,需要的rand次数就会变多。

实际上,当我们删除一个元素造成空洞的时候,完全可以将最后的元素调整到空洞位置。

查询由于我们给的是value,要查是否存在,所以必须要有一个hash寸的是value->index,而等概率返回则要求我们需要一个index->value的hash。

总结:设计hash1:value->index,hash2:index->value

查询:直接用value在hash1内查询即可

改动:给定一个value,在hash1中找到他的索引,再根据这个索引在hash2中找到它,改两个hash的value域即可。

增加:hash1.insert(value,++size);hash2.insert(size,value)。

删除:

A.删除的是尾元素,直接删除

B.得到hash2中size对应的value',将hash2和hash1中删除位置的value变更为value'。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个哈希表应用代码,具体实现方式和语言可能有所不同,这里提供一个伪代码的示例: ``` // 定义哈希表结构 struct HashTable { int size; // 哈希表大小 List *table; // 存储链的数组 }; // 初始化哈希表 HashTable *hash_init(int size) { HashTable *hash = malloc(sizeof(HashTable)); hash->size = size; hash->table = calloc(size, sizeof(List)); return hash; } // 计算哈希值 int hash_func(char *key) { int hash = 0; for (int i = 0; key[i]; i++) { hash = (hash << 5) + hash + key[i]; } return hash; } // 在哈希表中插入键值对 void hash_insert(HashTable *hash, char *key, int value) { int index = hash_func(key) % hash->size; List *list = &hash->table[index]; list_add(list, key, value); } // 在哈希表中查找键对应的值 int hash_lookup(HashTable *hash, char *key) { int index = hash_func(key) % hash->size; List *list = &hash->table[index]; ListNode *node = list_find(list, key); if (node) { return node->value; } else { return -1; // 示没找到 } } // 销毁哈希表 void hash_destroy(HashTable *hash) { for (int i = 0; i < hash->size; i++) { list_destroy(&hash->table[i]); } free(hash->table); free(hash); } ``` 在这个示例中,我们使用链来解决哈希冲突。`hash_func` 函数用于计算字符串的哈希值,通过取模运算将其映射到哈希表的某个位置。`hash_insert` 函数用于在哈希表中插入键值对,如果发生了哈希冲突,则通过链解决。`hash_lookup` 函数用于查找键对应的值,如果没找到则返回 -1。最后,`hash_destroy` 函数用于销毁哈希表

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值