哈希表及其常用算法(代码实现)

转载自—>http://blog.csdn.net/wangxu_zju_2010/article/details/7489548


整理了一下Hash表相关内容,如下:

Hash 表是使用 O(1) 时间进行数据的插入删除和查找,但是 hash 表不保证表中数据的有序性,这样在 hash 表中查找最大数据或者最小数据的时间是 O(N) 。


1 寻址和 hash 函数

         理想状态下 hash 足够大,每一数据保存在一个 hash 存储单元内,这样对于插入删除和查找某一个数据就可以直接得到。但是现实情况下 hash 表不可能无限大,而且理论上保存的数据的个数是没有限制的,这样保存的数据的数量就远远大于 hash 表的存储单元的数量。

         为了实现在 O(1) 内对数据进行插入删除和查找,就必须将一个数据映射到 hash 表中的固定位置,这个映射函数就是 hash 函数。 Hash 函数通过对数据进行计算得到一个在 hash 表中的位置地址。

 

图 1.1 理想的 hash 表

         要选择较好的 hash 函数,以及 hash 表存储单元的数量,这样才能使保存在 hash 表中的数据均匀分布。

         理想状态不太可能实现,由于存储的数据数量远远大于 hash 表存储单元的数量,所以再好的 hash 函数也可能使不同的数据得到相同的映射位置,这就造成了冲突。但是好的 hash 函数可以将这种冲突降到最低。

2 分离链接

         解决这种冲突的第一种方法是借助链表来实现,就是将数据实际存放在与 hash 表存储单元相链接的链表中,而不是 hash 的存储单元中。

 

图 2.1 分离链表

         当产生冲突的时候,将两个数据都链接在同一 hash 存储单元保存的链表中。当一个存储单元保存的链表中有多个数据的时候,对于链表后面的数据的查找添加和删除就是不是严格意义上的 O(1) 了。一个好的 hash 函数可以使得这个链表很短。最坏情况下,当所有的数据都保存在一个 hash 单元指定的链表中的时候,那么这个 hash 就和链表一样了。


3 开放地址

         使用开放地址方法解决冲突的时候,数据仍然保存在 hash 表的存储单元中,但是当冲突发生的时候,要再次计算新的地址。

         常用的开放地址法是线性探查,就是当对一个数据进行插入删除或者查找的时候,通过 hash 函数计算,发现这个位置不是要找的数据,这时候就检查下一个存储单元,一直找到要操作的数据为止。

         除了线性探查外还有二次探查,再 hash 等等方法,都是当一次计算得到的位置不是要找到的数据的时候,怎样再次确定新的位置。


4 完全 hash 表

         采用分离链表的方式解决冲突的时候,当多个数据被映射到一个地址的时候,它们就形成了一个链表,要操作这其中的一个数据,那么就必须在这个链表中进行操作。如果 hash 函数选择的好的话,链表会很短,这样的操作近似 O(1) ,但并不是精确的等于 O(1) ,它与链表的长度有关。对于数据的访问的最坏情况的访问也是 O(1) 的 hash 叫做完全 hash 表。

         这样的 hash 表是一个两级的 hash 表,第一级的 hash 与使用分离链接方法的 hash 一样,但是 hash 存储单元中指向的不是一个链表,而是另一个 hash 表。

 

图 4.1 完全 hash 表

         要小心的选择一级以及二级的 hash 函数可以完全保证在二级 hash 表中不会出现冲突。

5 常用算法

  1. 直接定址法 :地址集合 和 关键字集合大小相同
  2. 数字分析法 :根据需要hash的 关键字的特点选择合适hash算法,尽量寻找每个关键字的 不同点
  3. 平方取中法:取关键字平方之后的中间极为作为哈希地址,一个数平方之后中间几位数字与数的每一位都相关,取得位数由表长决定。比如:表长为512,=2^9,可以取平方之后中间9位二进制数作为哈希地址。
  4. 折叠法:关键字位数很多,而且关键字中每一位上的数字分布大致均匀的时候,可以采用折叠法得到哈希地址,
  5. 除留取余法除P取余,可以选P为质数,或者不含有小于20的质因子的合数
  6. 随机数法:通常关键字不等的时候采用此法构造哈希函数较恰当。

 实际工作中需要视不同的情况采用不同的hash函数:

  1. 考虑因素:计算哈希函数所需要的时间,硬件指令等因素。
  2. 关键字长度
  3. 哈希表大小
  4. 关键字分布情况
  5. 记录查找的频率。(huffeman树)
具体代码如下:

[cpp]  view plain  copy
 print ?
  1. "font-size:16px;">#include  
  2. #include  
  3. #include  
  4. struct HashTable;  
  5. struct ListNote;  
  6. typedef struct HashTable *HashTbl;  
  7. typedef struct ListNote *Position;  
  8. typedef Position List;  
  9. int Hash(int key,int tablesize);  
  10. int NextPrime(int x);  
  11. HashTbl InitalizeTable(int TableSize);  
  12. void DestroyTable(HashTbl H);  
  13. Position Find(int key,HashTbl H);  
  14. void Insert(int key, HashTbl H);  
  15. void Delete(int key,HashTbl H);  
  16. struct HashTable{   
  17.     int TableSize;  
  18.     Position *TheList;  
  19. };  
  20. struct ListNote{  
  21.     int element;  
  22.     Position next;  
  23. };  
  24. int Hash(int key,int tablesize){  
  25.     return key%tablesize;  
  26. }  
  27. int NextPrime(int x){  
  28.     int flag;  
  29.     while(1){  
  30.         flag = 0;  
  31.         int i;  
  32.         int n = sqrt((float)x);  
  33.         for(i = 2 ;i <= n;i++){  
  34.             if(x % i == 0){  
  35.                 flag = 1;  
  36.                 break;  
  37.             }  
  38.         }  
  39.         if(flag == 0)  
  40.             return x;  
  41.         else  
  42.             x++;  
  43.     }  
  44. }  
  45. HashTbl InitalizeTable(int TableSize){  
  46.     if(TableSize <= 0){  
  47.         printf("散列大小有问题\n");  
  48.         return NULL;  
  49.     }  
  50.     HashTbl table = (HashTbl)malloc(sizeof(struct HashTable));  
  51.     if(table == NULL)  
  52.         printf("分配失败");  
  53.     table->TableSize = NextPrime(TableSize);  
  54.     table->TheList = (Position*)malloc(sizeof(List) * table->TableSize);  
  55.     if(table->TheList == NULL)  
  56.         printf("分配失败");  
  57.     table->TheList[0] = (Position)malloc(table->TableSize*sizeof(struct ListNote));  
  58.     if(table->TheList == NULL)  
  59.         printf("分配失败");  
  60.     int i;  
  61.     for(i = 0;i < table->TableSize;i++){  
  62.         table->TheList[i] = table->TheList[0] + i;  
  63.         table->TheList[i]->next = NULL;  
  64.     }  
  65.     return table;  
  66. }  
  67. Position Find(int key,HashTbl H){  
  68.     Position p;  
  69.     List L = H->TheList[Hash(key,H->TableSize)];  
  70.     p = L->next;  
  71.     while(p != NULL && p->element != key)  
  72.         p = p->next;  
  73.     if(p == NULL)  
  74.         return L;  
  75.     else  
  76.         return p;  
  77. }  
  78. void Insert(int key,HashTbl H){  
  79.     Position p,NewCell;  
  80.     p = Find(key,H);  
  81.     if(p->element != key){  
  82.         NewCell = (Position)malloc(sizeof(struct ListNote));  
  83.         if(NewCell == NULL)  
  84.             printf("分配失败");  
  85.         else{  
  86.             p = H->TheList[Hash(key,H->TableSize)];  
  87.             NewCell->next = p->next;  
  88.             p->next = NewCell;  
  89.             NewCell->element = key;  
  90.         }  
  91.     }  
  92.     else  
  93.         printf("已经存在该值了\n");  
  94. }  
  95. void Delete(int key,HashTbl H){  
  96.     Position p ,NewCell;  
  97.     p = Find(key,H);  
  98.     if(p->element == key){  
  99.         NewCell = H->TheList[Hash(key,H->TableSize)];  
  100.         while(NewCell->next != p)  
  101.             NewCell = NewCell->next;  
  102.         NewCell->next = p->next;  
  103.         free(p);  
  104.     }  
  105.     else  
  106.         printf("没有该值");  
  107. }  
  108. int main(){  
  109.     HashTbl table = InitalizeTable(10);  
  110.     Position p = NULL;  
  111.     p = Find(10,table);  
  112.     printf("%d\n",p->element);  
  113.     Insert(55,table);  
  114.     Insert(90,table);  
  115.     Insert(35,table);  
  116.     Insert(33,table);  
  117.     p = Find(55,table);  
  118.     printf("%d\n",p->element);  
  119.     p = Find(33,table);  
  120.     printf("%d\n",p->element);  
  121.     Delete(33,table);  
  122.     Delete(44,table);  
  123.     system( "pause" );  
  124.     return 0 ;  
  125. }  

你可能感兴趣的:(数据结构)