哈希表之二----链地址法创建哈希表

来源:互联网 发布:淘宝店铺怎么重新激活 编辑:程序博客网 时间:2024/05/31 18:54

解决哈希表地址冲突的一个方法是分离链接法,又称链地址法,是将所有关键字为同义词的记录存储在同一线性链表中。对于创建哈希表H,假设通过哈希函数产生的关键字在区间[0,m-1]上,则建立一个返回值为链表类型的数组H->TheLists[m],数组每个元素的初始状态都为都为空(或只包含哑元结点),凡哈希地址为i的记录都插入到头指针为H->TheLists[i] (i从0到m-1)的线性链表中,关键字插入到对应链表的位置时保证同义词在同一线性链表中按关键字有序

例如:已知一组关键字为19,14,23,01,68,20,84,27,55,11,10,79,则按哈希函数H(key)=key MOD 13 ,通过链地址法处理冲突构造哈希表如下图所示



在算法实现过程中,声明一个哈希表结构体,内部嵌套链表结构体

struct ListNode//链表的结构体{ElementType Element;position Next;};typedef position List;struct HashTb{int TableSize;//哈希表的大小,即哈希表中链表的个数List *TheLists;//链表指针数组,数组的每个元素为一个链表指针};

下面是具体的代码实现:(这里的程序对哈希表中的每个链表都使用了头节点,而且对插入时还保持每个链表有序,这两点在实际应用中是不可取的,因此还需对程序进行优化)

hashtable.h

#define MINTBSIZE 2#define TBSIZE 13#ifndef  _Hashtable_Htypedef int ElementType;typedef unsigned int index;struct ListNode;typedef struct ListNode *position;struct HashTb;typedef struct HashTb *HashTable;HashTable InitializeTable(int tablesize);void DestroyTable(HashTable H);position Find(ElementType key,HashTable H);void Insert(ElementType key,HashTable H);void Delete(ElementType key,HashTable H);void MakeEmpty(HashTable H);void PrintTable(HashTable H);//ElementType Retrieve(position p);#endif

hashtable.c

#include "hashtable.h"#include<stdio.h>#include<stdlib.h>struct ListNode//链表的结构体{ElementType Element;position Next;};typedef position List;struct HashTb{int TableSize;//哈希表的大小,即哈希表中链表的个数List *TheLists;//链表指针数组,数组的每个元素为一个链表指针};index Hash(ElementType key,int tablesize)//返回关键字对应的哈希函数的值,即为该关键字在哈希表中的链表的位置(是第X个链表){return key % tablesize;//哈希函数}HashTable InitializeTable(int TableSize)//初始化哈希表{HashTable H;int i;if(TableSize < MINTBSIZE){printf("Tablesize is too small!\n");exit(0);}H=(struct HashTb*)malloc(sizeof(struct HashTb));if(NULL==H){printf("insufficient memory!\n");}H->TableSize=TBSIZE;//-----------------------------------------------------------------------------类似于定义二维动态数组H->TheLists=(List *)malloc(sizeof(List)* H->TableSize);//一个包含链表指针的数组,每个数组元素为一个链表指针if(NULL==H->TheLists){printf("Insufficient memory!\n");exit(0);}for(i=0;i < H->TableSize;i++){H->TheLists[i]=(List)malloc(sizeof(struct ListNode));if(NULL==H->TheLists[i]){printf("Insufficient memory!\n");exit(0);}H->TheLists[i]->Next=NULL;}//--------------------------------------------------------------------------------return H;}position Find(ElementType key,HashTable H)//查找哈希表中是否存在关键字为key 的结点,存在则返回其位置,不存在则返回NULL{position p;List L;L=H->TheLists[Hash(key,H->TableSize)];for(p=L->Next;p!=NULL;p=p->Next){if(p->Element==key)break;}return p;} void Insert(ElementType key,HashTable H)//向哈希表中插入关键字key{List newnode=(List)malloc(sizeof(struct ListNode));List p;position pre;if(NULL==newnode){printf("Insufficient memory !\n");exit(0);}newnode->Element=key;if(!Find(key,H))//若插入前哈希表中不存在关键字k,将该关键字插入到哈希表中,这里是将关键字按从小到大的顺序插入到对应位置{List L=H->TheLists[Hash(key,H->TableSize)];//找到该关键字在哈希表中的对应位置for(p=L;p!=NULL;pre=p,p=p->Next){if(p->Element >= newnode->Element)break;}        newnode->Next=pre->Next;pre->Next=newnode;printf("the key value %3d has inserted successfully!\n",key);}}void Delete(ElementType key,HashTable H)//删除哈希表中特定的关键字{if(!Find(key,H)){printf("%d can't be existed in this hashtable!\n",key);exit(0);}else{position pre,p,tmp;List L=H->TheLists[Hash(key,H->TableSize)];for(p=L;p!=NULL;pre=p,p=p->Next){if(p->Element==key)break;}tmp=p;pre->Next=p->Next;free(tmp);//利用free函数来释放删除的结点所占用的内存}printf("\nthe key value %3d has been deleted!\n",key);}void MakeEmpty(HashTable H)//置空哈希表{int i;position tmp,p;for(i=0;i<H->TableSize;i++){List L=H->TheLists[i];p=L->Next;while(p!=NULL){tmp=p;p=p->Next;free(tmp);}L->Next=NULL;}printf("The hashtable has been made empty!\n");}void DestroyTable(HashTable H)//销毁哈希表{int i;position tmp;for(i=0;i< H->TableSize;i++){       position p=H->TheLists[i];   while(p!=NULL)   {  tmp=p->Next;  free(p);  p=tmp;    }}free(H->TheLists);free(H);printf("\nthe hashtable has been destroyed!\n");}void PrintTable(HashTable H)//打印哈希表{List L;position p;int i;printf("\nThe hashtable based link address is below:\n");for(i=0;i< H->TableSize;i++){   L=H->TheLists[i];if(L->Next==NULL)            continue;printf("第 %3d 个位置的链表:",i);for(p=L->Next;p!=NULL;p=p->Next){printf("%3d ",p->Element);}printf("\n");}printf("其余位置的链表为空!\n");}
main.c
#include "hashtable.h"#include <stdio.h>int main(void){HashTable H;int i;int a[12]={19,14,23,01,68,20,84,27,55,11,10,79};H=InitializeTable(13);//插入19,14,23,01,68,20,84,27,55,11,10,79到哈希表中for(i=0;i<12;i++){Insert(a[i],H);}PrintTable(H);Delete(11,H);PrintTable(H);MakeEmpty(H);return 0;}

运行:



在实际应用中,由于两方面的原因,通常采用无序的地址链:

  1.插入速度快:计算散列函数,为节点分配内存,把节点链接到相应的地址链的头部

  2.每个链表都可以向对堆栈那样工作,因此我们可以轻易的删除最近插入的元素,因为每个元素在每个地址链的开始位置。

当我们实现一个有嵌套作用域的符号表时,比如在一个编译器中,这种操纵就显得至关重要。


分离链接哈希表算法的缺点:

1.需要指针,由于给新单元分配地址需要时间,因此就导致算法的速度多少有些缓慢

2.算法实际上还要求了对另一种数据结构的实现,因为向哈希表的初始化,关键字的插入、删除等操作过程都会涉及链表的操作。

0 0
原创粉丝点击