/*hash表之拉链法处理冲突:*/
方法一:(不推荐)
#define ARRLEN 17
#define NAMELEN 20
#define ADDRLEN 20
typedef struct _rec
{
char name[NAMELEN];
char addr[ADDRLEN];
struct _rec *next;
} rec;
//hash函数,线性定址法和余数法结合。
//得到的地址为数组的下标。
//count为hash表长,即模。
int hash(char *name, int count)
{
int hashcode = 0;
int i;
int len = strlen(name);
for(i = 0; i < len; i++)
{
hashcode = hashcode * i + name[i];
}
return hashcode % count ;
}
/*
添加一个记录,如果这个位置已有元素,则在该元素后拉链,添加到链中。
数组中的每个元素的值为 结构体元素的指针。
并且数组中的每个元素存储的指针域为带有头节点的链表的头节点。
数组中的每个元素时不存储数据域的,因为头节点没有数据域。
所以数组名为结构体元素的二级指针。
*/
void hash_insert(rec **hashtable, int count, rec *record)
{
int hashcode = hash(record->name, count);
rec *tmp;
//hash地址没有被占用,直接填充。
if(hashtable[hashcode] == NULL)
{
if( (hashtable[hashcode] = (rec *)malloc(sizeof(rec))) == NULL)
{
fprintf(stderr, "malloc error\n");
}
else
{
strcpy(hashtable[hashcode]->name, record->name);
strcpy(hashtable[hashcode]->addr, record->addr);
hashtable[hashcode]->next = NULL;
}
}
else
{//被占用了,则填充在后面的链的结尾处。
tmp = hashtable[hashcode];
while(tmp->next != NULL)
{
tmp = tmp->next;
}//定位到链尾。
if( (tmp->next = (rec *)malloc(sizeof(rec))) == NULL)
fprintf(stderr, "malloc error\n");
else
{
strcpy(tmp->next->name, record->name);
strcpy(tmp->next->addr, record->addr);
tmp->next->next = NULL;
}
}
}
/*
hash_search:
*/
int hash_search(rec **hashtable, int count, rec *record)
{
int hashcode = hash(record->name, count);
rec *tmp = hashtable[hashcode];
while(tmp != NULL)
{
if(strcmp(tmp->name, record->name) == 0)
{
strcpy(record->addr, tmp->addr);
return hashcode;
}
//else tmp=temp->next;
}
return -1;
}
/*
删除某个记录,
*/
void hash_delete(rec **hashtable, int count, char *name)
{
int hashcode = hash(name, count);
rec *tmp1, *tmp2;
//没有该记录。
if(hashtable[hashcode] == NULL)
return;
//在第一个位置就找到记录。
else if(hashtable[hashcode] != NULL &&
strcmp(hashtable[hashcode]->name, name) == 0)
{
free(hashtable[hashcode]);
hashtable[hashcode] = NULL;
}
else
{
tmp1 = hashtable[hashcode];
while(tmp1->next != NULL && strcmp(tmp1->next->name, name) != 0)
tmp1 = tmp1->next;
if(tmp1->next != NULL)
{
tmp2 = tmp1->next;
tmp1->next = tmp2->next;
free(tmp2);
}
}
}
int menu_select(void)
{
char s[80];
int c;
do
{
printf("1. Enter a record\n");
printf("2. Search a record\n");
printf("3. Delete a record\n");
printf("4. Quit\n");
printf("Enter a choice:\n");
fgets(s, 80, stdin);
c = atoi(s);
} while(c < 1 || c > 5);
return c;
}
void rec_insert(rec **hashtable, int count)
{
rec tmp;
int len;
printf("Enter the name:\n");
fgets(tmp.name, NAMELEN, stdin);
printf("Enter the addr:\n");
fgets(tmp.addr, ADDRLEN, stdin);
len = strlen(tmp.name);
if(tmp.name[len -1] == '\n')
tmp.name[len - 1] = '\0';
len = strlen(tmp.addr);
if(tmp.addr[len -1] == '\n')
tmp.addr[len -1] = '\0';
tmp.next = NULL;
hash_insert(hashtable, count, &tmp);
}
void rec_search(rec **hashtable, int count)
{
rec tmp;
int len;
int loc;
printf("Enter the name:\n");
fgets(tmp.name, NAMELEN, stdin);
len = strlen(tmp.name);
if(tmp.name[len -1] == '\n')
tmp.name[len -1] = '\0';
if( (loc = hash_search(hashtable, count, &tmp)) != -1)
{
printf("%s\n%s\n", tmp.name, tmp.addr);
}
else
printf("can not found\n");
}
void rec_delete(rec **hashtable, int count)
{
char s[NAMELEN];
int len;
printf("Enter the name:\n");
fgets(s, NAMELEN, stdin);
len = strlen(s);
if(s[len -1] == '\n')
s[len -1] = '\0';
hash_delete(hashtable, count, s);
}
int main(void)
{
rec *a[ARRLEN];
int choice;
memset(a, 0, sizeof(a));
while(1)
{
choice = menu_select();
switch(choice)
{
case 1:
rec_insert(a, ARRLEN);
break;
case 2:
rec_search(a, ARRLEN);
break;
case 3:
rec_delete(a, ARRLEN);
break;
case 4:
exit(0);
}
}
}
----------------------------
(二)步骤:
(1)构建一个链表的节点的结构体ListNode;包含两个字段一个字段是 关键字,一个是next;
那么使用ListNode*类型的变量,就可以保存一个带有头节点的链表了。
(2)构建一个HashTable的结构体;包含两个字段,一个是hashTable的大小,size,还有一个是
ListNode*的数组 ListNode** lists,即 lists[i] 就是 hash值为i的链表,i为通过hash函数得到的 hash地址 。
(注意:HashTable数组中的每个元素存储的是链表的 头指针,并没有包含关键字的值, 关键字都是存储在链表的头指针后面的节点中 )
(3)构造hash函数,如 int Hash(KeyType key, int prime);
取余法,得到地址,返回。
(4)初始化HashTable,数组中每个元素为链表的头结点。
(5)int hashSearch(HashTable h , KeyType key);
可有key,根据hash函数,得到key所指的链表的头指针,然后依次遍历该链表,看是否可以查到。
(6) hashInsert(HashTabel h ,KeyType key)
首先调用hashSearch函数,如果,没有查找到,则插入。
---------------------------
方法二:(推荐)
/*
*哈希表 拉链法
*/
#include<stdio.h>
#include<stdlib.h>
#define MinTableSize 10
typedef int ElemType;
typedef unsigned int Index;
typedef struct ListNode
{
ElemType element;
struct ListNode *next;
}*Position;
typedef Position List;
/*
类型说明:
ListNode类型:
List=Position=ListNode*;
HashTbl*=HashTable;
List* TheLists,所以TheList是ListNode**类型的;
*/
typedef struct HashTbl
{
int TableSize;
List *TheLists;
}*HashTable;
/*因为表长通常都是prime*/
//这个写的好像不对。
int NextPrime(int N)
{
int i;
if(N%2==0)
N++;
for(;;N+=2)
{
for(i=3;i*i<=N;i+=2)
if(N%i==0)
return 0;
return N;
}
}
/*hash函数*/
Index Hash(ElemType Key,int TableSize)
{
return Key%TableSize;
}
/*
初始化hash表。
hash表的数组名是ListNode**;
表中的每个元素为listNode*;
初始化时每个元素都是一个空链,所以每个元素的next为null;
*/
HashTable InitializeTable(int TableSize)
{
HashTable H;
int i;
if(TableSize<MinTableSize)
{
printf("Table size too small!\n");
return NULL;
}
/*Allocate table*/
H=(HashTable)malloc(sizeof(struct HashTbl));
if(NULL==H)
printf("Out of space!!!\n");
H->TableSize=NextPrime(TableSize);
H->TheLists=(List *)malloc(sizeof(List)*H->TableSize);
if(NULL==H->TheLists)
{
printf("Out of space!!!\n");
free(H);
return NULL;
}
for(i=0;i<H->TableSize;i++)
{
H->TheLists[i]=(Position)malloc(sizeof(struct ListNode));
if(NULL==H->TheLists[i])
printf("Out of space!!!\n");
else
H->TheLists[i]->next=NULL;
H->TheLists[i]->element=0;//哈希表中所有元素的key初始化为0
}
return H;
}
/*hash查找*/
Position Find(ElemType Key,HashTable H)
{
Position p;
List L;
L=H->TheLists[Hash(Key,H->TableSize)];
p=L->next;
while(p!=NULL&&p->element!=Key)
p=p->next;
return p;
}
/*
hash插入
hashTable中的每个元素存储的是带有头节点的链表的头节点。
头节点的数据域是没有数据的。
所以如果一个存储一个数据,数据存储的是在hashTable元素的next节点中。
*/
void Insert(ElemType Key,HashTable H)
{
Position pos,newCell;
List L;
pos=Find(Key,H);
if(NULL==pos)/*没有找到关键字key*/
{
newCell=(Position)malloc(sizeof(struct ListNode));
if(NULL==newCell)
printf("Out of space!!!");
else
{
L=H->TheLists[Hash(Key,H->TableSize)];
newCell->next=L->next;
newCell->element=Key;/*头插法*/
L->next=newCell;
}
}
}
/*
删除hash表。
*/
void DestroyTable(HashTable H)
{
int i;
for(i=0;i<H->TableSize;i++)
{
Position p=H->TheLists[i];
Position temp;
while(p!=NULL)
{
temp=p->next;
free(p);
p=temp;
}
}
free(H->TheLists);
free(H);
}
void printHash(HashTable H,int len)
{
int i;
for(i=0;i<len;i++)
{
Position p=H->TheLists[i];
while(p)
{
printf("address=%d value=%d\n",i,p->element);
p=p->next;
}
}
}
int main()
{
HashTable H;
Position p=NULL;
int array[]={19,14,23,01,68,20,84,27,55,11,10,79};
int len=sizeof(array)/sizeof(array[0]);
int i;
ElemType k;
H=InitializeTable(len);
for(i=0;i<len;i++)
{
Insert(array[i],H);
}
printHash(H,len);
printf("\n\n");
printf("please input the value which need find:");
scanf("%d",&k);
p=Find(k,H);
if(p)
printf("%d",p->element);
else
printf("cannot find the value!");
printf("\n\n");
printf("free the table\n");
DestroyTable(H);
printf("it's done!!!");
printf("\n\n");
return 0;
}