Hash Table的补充

本文探讨了哈希表的实现方式,对比了开放式寻址与链地址法解决哈希冲突的效果,并讨论了哈希函数的设计原则及质数表大小的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

      上一篇文章已经说了Hash Table作为一个数组以开放式寻址的方式实现 ,用开放式寻址去解决Key的冲突比较麻烦,不管是用线性探针,二次方探针,即便是二次Hash也还是有缺陷,一旦数组变的比较满的时候,算法的效率就会大打折扣,而且用开放式寻址就不能真正意义上的去删除一个数据项,如果删除一个数据项,以后的搜索某个数据就会发生错误。

      用Linked List来代替数组中的元素就可以比较好的解决Key冲突的问题,而为此付出的代价仅仅是代码复杂一点,要在数组的基础上实现Linked List。但是得到的好处却是非同凡响的。

     用Linked List来实现Hash Table的代码就不在赘述了,虽然比Open Addressing要复杂,但是也不难,相对与Binary Tree来说,还是要简单点的。

     下面再来说下Hash Function,为什么要用Hash Function,最根本的目的当然是为了搜索起来更加的节省时间,所以Hash Function的算法不能复杂,越简单越好,多用些位操作则更好,比如将2进制数右移一位以达到除以2的目的。一个所谓的完美的Hash Function可以将每一个Key随机的映射到不同的位置上,这种情况只有在Key很好的分布,以及范围足够的小,可以直接作为Index.当然这种情况很少见,就算出现了也会直接用数组,何必去用Hash Table呢!用Hash Table大多都是要把比较大的Key压缩到一个相对容量较小的数组中。总的来说一个好的Hash Function要做到简单,快速,排除Key的非有用数据部分,包括Key的所有有用的数据部分。

    还有就是Table Size最好是质数,Table Size是质数在二次方探针和二次Hash中很重要,但是在其他的搜索方式中也最好用质数,因为有的时候Key并不是一个随机的数,比如,有一串Key,他们都是50的倍数,如果Table Size是50,那么所有的数都会被Hash到同一个地方,导致了聚合严重。在这种情况下用53最为数组的大小就不会导致聚合情况的严重了。

   如果探讨到Hash Table的效率,在大多数的情况下用Linked List实现的Hash Table效率要好点,因为不会像开放式寻址那样去不断的寻找特定的Cell,而是只要在特定的Linked List里面去寻找。当然对于很小的Hash Table,用开放式寻址的线性探针是个不错的选择,因为他的编码简单!

#include <stdio.h> #define TABLE_SIZE 18 // 哈希表大小,对应地址范围0 - 17 // 初始化哈希表 void initHashTable(int hashTable[]) { for (int i = 0; i < TABLE_SIZE; i++) { hashTable[i] = -1; // -1表示该位置为空 } } // 哈希函数 int hashFunction(int key) { return key % 16; } // 插入关键字到哈希表 void insert(int hashTable[], int key) { int index = hashFunction(key); int originalIndex = index; int count = 1; while (hashTable[index] != -1) { // 位置不为空,发生冲突 index = (index + 1) % TABLE_SIZE; // 线性探测下一个位置 if (index == originalIndex) { printf("哈希表已满,无法插入\n"); return; } count++; } hashTable[index] = key; } // 在哈希表中查找关键字 void search(int hashTable[], int key) { int index = hashFunction(key); int originalIndex = index; int count = 1; while (hashTable[index] != key && hashTable[index] != -1) { // 未找到且位置不为空 index = (index + 1) % TABLE_SIZE; // 线性探测下一个位置 if (index == originalIndex) { printf("-1 %d\n", count); // 未找到,返回-1和比较次数 return; } count++; } if (hashTable[index] == key) { printf("%d %d\n", index, count); // 找到,返回地址和比较次数 } else { printf("-1 %d\n", count); // 未找到,返回-1和比较次数 } } int main() { int hashTable[TABLE_SIZE]; initHashTable(hashTable); int key; for (int i = 0; i < 11; i++) { scanf("%d", &key); insert(hashTable, key); } int searchKey; scanf("%d", &searchKey); search(hashTable, searchKey); return 0; } 改错
最新发布
03-29
程序存在以下几个问题: ### 1. **哈希函数设计不合理** 当前的 `hashFunction` 使用了固定的模数 (`% 16`),而实际表格大小为 `TABLE_SIZE = 18`。这可能导致某些索引从未被使用。 #### 修改建议: 将哈希函数改为取模于 `TABLE_SIZE` 的值: ```c int hashFunction(int key) { return key % TABLE_SIZE; } ``` --- ### 2. **线性探测环回判断条件错误** 在插入和查找过程中,如果检测到当前探查的位置回到了起点,则认为哈希表已经满了或未找到目标键值对。然而,这里的计数变量 `count` 并没有合理地反映冲突情况。 #### 修改建议: 将计数逻辑调整得更清晰,并修复循环终止条件。例如,在插入操作中可以加入更多调试信息确认状态。 修改后的 `insert` 函数示例: ```c void insert(int hashTable[], int key) { int index = hashFunction(key); int originalIndex = index; // 如果当前位置已被占用则尝试其他位置直到找到空位或者遍历完整个数组. while (hashTable[index] != -1 && hashTable[index] != key){ index=(index+1)%TABLE_SIZE; if(index==originalIndex){ printf("Hash table is full,cannot insert.\n"); return ; } } // 存储key 或更新已有数据(避免重复) hashTable[index]=key ; } ``` 类似更改也需应用于搜索部分。 --- ### 改正后整体代码结构概览 以下是修正版本的核心内容摘录(仅列出改动之处): ```cpp #define TABLE_SIZE 18 // Hash function using the proper modulo size now. int hashFunction(int key) { return key % TABLE_SIZE; } // Insertion logic with better handling of overflow and duplicates. void insert(int hashTable[], int key) { int index = hashFunction(key); int originalIndex = index; while ((hashTable[index]!=-1)&&(hashTable[index]!=key)){ index = (index +1 ) % TABLE_SIZE ; // Check whether we've made a complete cycle around. if (index == originalIndex ){ printf ("Hash table is full, cannot insert .\n "); return ; } } // Store or update existing data at found location . hashTable [index ]=key ; } // Searching procedure adjusted accordingly too... void search(int hashTable [], int key ){ ...... } ``` 完整的解决方案包括上述细节补充及所有功能模块的一致性调整。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值