数据结构:散列表(哈希表)

来源:互联网 发布:客户经理好用软件 编辑:程序博客网 时间:2024/06/06 04:13
概念:
    在理想情况下,散列表数据结构就是一个某种数据类型的具有固定大小的数组。在数组用于保存某一集合的数据。集合中的元素根据某种运算(散列函数)得到相应的小标。理想情况下,不同的元素对应不同的下标。如果不同的元素对应相同的下标,那么我们称这种情况为冲突。
    散列函数多种多样,根据不同的情况选择不同的函数,散列函数一般有哪些,自己查课本。那么出现冲突时,有几种解决方法,这里,我们将讨论最简单的两种。(分离链表法,开放定址法)

一、分离链表法。
    做法:可以把散列表看成结构体数组,并分别形成一个链表。将散列到同一个值的所有元素保留到同一个链表中。

相关的代码如下所示:

#include<stdio.h>#include<stdlib.h>//程序中不考虑malloc失败的情况,老是检查,太累了。typedef struct listnode{struct listnode *next;int element;}listnode;typedef struct hashtable{int tablesize;listnode **list;}hashtable;hashtable *initial_hashtable(int );int hashfuntion(int,int);listnode *find(int,hashtable *);void insert(int,hashtable *);void Retrieve(hashtable *);void DestoryTable(hashtable *);hashtable *initial_hashtable(int tablesize){int i;hashtable *h;h=(hashtable *)malloc(sizeof(hashtable));h->tablesize = tablesize;//开始,很好奇明明h->list申请了tablesize个地址空间,可是为什么//h->list[i]还要申请地址空间呢?他们到底有什么区别呢?// h->list申请的是指向listnode *的地址空间//而h->list[]申请的是listnode *的地址空间,是不一样的概念h->list = (listnode **)malloc(sizeof(listnode)*tablesize);for(i=0;i<tablesize;i++){h->list[i]=(listnode *)malloc(sizeof(listnode));h->list[i]->next=NULL;h->list[i]->element =0;}return h;}//哈希函数为对tablesize取模int hashfuntion(int key,int tablesize){return key%tablesize;}listnode *find(int key,hashtable *h){int index;listnode *p;index = hashfuntion(key,h->tablesize);p = h->list[index]->next;while(p!=NULL){if(p->element==key){return p;}p=p->next;}return NULL;}void insert(int key,hashtable *h){int index;listnode *p,*newnode;p=find(key,h);if(p!=NULL){return;}index = hashfuntion(key,h->tablesize);p= h->list[index];while(p->next!=NULL){p=p->next;}newnode=(listnode *)malloc(sizeof(listnode));newnode->element=key;newnode->next=NULL;p->next=newnode;return;}void Retrieve(hashtable *h){int i;listnode *p;for(i=0;i<h->tablesize;i++){p=h->list[i];while(p->next!=NULL){p=p->next;printf("%d ",p->element);}printf("%s","---");}}void DestoryTable(hashtable *h){int i;listnode *p,*L;for(i=0;i<h->tablesize;i++){p=h->list[i];while(p->next!=NULL){L=p;p=p->next;free(L);}}free(h->list);free(h);}void main(){//测试代码int a[]={0,1,2,3,4,5,6,7,8,9,10,11,12};int i;hashtable *h;h= initial_hashtable(5);for(i=0;i<13;i++){insert(a[i],h);}Retrieve(h);}
二、对于开放定址法,一般有以下三种方法解决冲突
1,利用线性探测法构造散列表
    题目:已知一组关键字为(26,36,41,38,44,15,68,12,06,51),用除余法构造散列函数,用线性探查法解决冲突构造这组关键字的散列表。
  解答:为了减少冲突,通常令装填因子α<=0.5。这里关键字个数n=10,不妨取m=19(一般是素数),此时α≈0.526,散列表为T[0..18],散列函数为:hi(key)=(key+i)%19。(i=[0...18])
     由除余法的散列函数计算出的上述关键字序列的散列地址为(7,17,3,0, 6,15,9,12,6,13)。
     前8个关键字插入时,其相应的地址均为开放地址,故将它们直接插入T[7],T[17),T[3],T[0],T[6),T[15],T[9]和T[12]中。
     当插入第9个关键字06时,其散列地址6已被关键字44占用。故探查h1=(6+1)%19=7,此地址也被关键字26占用,故探查h2=(6+2)%19=8
      所以将6放入T[8]中。
     当插入第10个关键字51时,其散列地址为开放地址。故将其插入到T[13]中。
2、  利用平方探测法构造散列表 
     平方探测是消除线性探测中一次聚集问题的冲突解决方法。 
     由上例可知,当hi(key)=(key+i)%19。(i=[0...18])改成hi(key)=(key+i^2)%19。(i=[0...18])就是了。
3、利用双散列构造散列表
     基本原理都是一样的,而双散列hi(key)=i*(key%19);但是这种做法是存在灾难性结果的,因为若key为19的话,则i不起作用,
     因此我们常常用hash(key)= R-(key%R) R为小于TableSize的素数。所以有hi(key)=i*(R-(key%R));
     所以hash()函数的选择相当重要。

我们以其中的第一种方法为例,写出以下代码:

//在散列表中删除操作一般都选择懒惰删除,所谓的懒惰就是在删除数的位置标记为//deleted状态#include<stdio.h>#include<stdlib.h>#define Legitimate 0#define Empty      1#define Deleted    2#define divided 19typedef struct HashEntry{int element;int info;}HashEntry;typedef struct HashTable{int TableSize;HashEntry *he;}HashTable;HashTable *Initial_Table(int);void DestroyTable(HashTable *);int Find(int ,HashTable *);void Insert(int ,HashTable *);int Retrieve(int ,HashTable *);int HashFuntion(int,int);void error(char *);void error(char *c){//略}void DestroyTable(HashTable *H){//略}int HashFuntion(int key,int i){return (key+i)%divided;}int Retrieve(int p,HashTable *H){//略。。return 0;}void Insert(int key,HashTable *H){int index;int i;for(i=0;i<divided;i++){index= HashFuntion(key,i);if(H->he[index].info == Empty){H->he[index].info = Legitimate;H->he[index].element = key;return;}}error("out of space");}int Find(int key,HashTable *H){int i;for(i=0;i<H->TableSize;i++){if(H->he[i].element ==key && H->he[i].info == Legitimate){return i;}}error("not be found");return 0;}HashTable *Initial_Table(int TableSize){int i;HashTable *H=(HashTable *)malloc(sizeof(HashTable));H->TableSize = TableSize;H->he=(HashEntry *)malloc(sizeof(HashEntry)*TableSize);for(i=0;i<TableSize;i++){H->he[i].element = 0;H->he[i].info = Empty;}return H;}void main(){//测试代码int i;int index;int a[]={26,36,41,38,44,15,68,12,06,51};HashTable *H = Initial_Table(19);for(i=0;i<10;i++){Insert(a[i],H);}index=Find(06,H);printf("06的散列地址是:%d",index);}


原创粉丝点击