哈希表是一种数据结构,它可以提供快速的插入操作和查找操作。不论哈希表中有多少数据,插入和删除都只需要接近常O(1)的时间级。
缺点:它是基于数组的,数据创建后难以扩展。
高级计算机语言的编译器,通常用哈希表保留符号表,符号表记录了程序员声明的所有变量和函数名,以及它们在内存中的地址,程序需要快速地访问这些名字,所以哈希表是理想的数据结构。
例如,想在内存中存储50000个英文单词,考虑用数组来存储,每个单词占据一个数组单元。
如何把代表单个字母的数字(a=1,b=2,c=3,...,z=26)组合成代表整个单词的数字呢?有许多方法,下面看两种有代表性的方法:
1.把单词每个字符的代码求和,假设约定单词有十个字母,那么,第一个单词a的编码为1,字典最后一个可能的单词是10个z,所有的字符编码的和是26*10=260。因此,单词编码的范围是1~260,但词典中有5000个单词,所以没有足够数组下标来索引那么多的单词,每个数组数据项大概要存储192个单词。
可以考虑每个数组数据项包含一个子数组或链表,但这个办法降低了存取速度,插入数据项确实很快,但要在192个单词中找到其中一个,速度就很慢了。
所以说,这个方法没有把单词分得足够开,所以结果数组能表达的元素太少,需要扩展数组表达下标的空间。
2.哈希化:可以通过把单词每个字母乘以7的适当次幂,再相加,使单词成为一个巨大的数字,然后使用取余操作,把得到的巨大整数范围转换成要存储内容的数组下标范围。
哈希函数:arrayIndex = hugeNumber%arraySize
哈希表:把一个大范围的数字哈希成一个小范围的数字,这个小的范围对应数组的下标,使用哈希函数向数组插入数据后,这个数组称为哈希表。
冲突
把巨大的数字空间压缩成较小的数字空间,必然需付出代价,即不能保证每个单词都映射到数组的空白单元,所以必须通过另外的方式解决这个问题。
1.开放地址法:当冲突发生时,通过系统的方法找到数组的一个空位,并把这个单词填入,而不是用哈希函数得到数组下标。
2.链地址法:创建一个存储单词链表的数组,数组内不直接存储单词,这样,当发生冲突时,新的数据项直接到这个数据下标所指的链表中。
开发地址法程序实现:
#define SUCCESS 1
#define UNSUCCESS 0
#define DUPLICATE -1
#define NULLKEY 0 //无记录标志
#define N 10 //数组元素个数
typedef int ElemType; //关键字为整型
int hashsize[]={11,19,29,37}; //哈希容量递增表,一个合适的素数序列
int m=0; //哈希表表长,全局变量
typedef struct
{
ElemType *elem;
int count;
int sizeindex;
}HashTable;
//构造一个空的哈希表
int InitHashTable(HashTable *H)
{
int i;
(*H).count=0;
(*H).sizeindex=0;
m=hashsize[0];
(*H).elem=(ElemType*)malloc(m*sizeof(ElemType));
if(!(*H).elem)
exit(0);
for(i=0;i<m;i++)
(*H).elem[i]=NULLKEY;
return 1;
}
//销毁哈希表
void DestroyHashTable(HashTable *H)
{
free((*H).elem);
(*H).elem=NULL;
(*H).count=0;
(*H).sizeindex=0;
}
//一个简单的哈希函数(m为表长)
unsigned Hash(ElemType K)
{
return K%m;
}
//开放定址法处理冲突
void collision(int *p,int d) //线性检测再散列
{
*p=(*p+d)%m;
}
//c用于计冲突次数
int SearchHash(HashTable H,ElemType K,int *p,int *c)
{
*p=Hash(K);
while((H.elem[*p]!=NULLKEY)&&(H.elem[*p] != K))
{
(*c)++;
if(*c<m)
collision(p,*c);
else
break;
}
if (K == H.elem[*p])
return SUCCESS;
else
return UNSUCCESS;
}
int InsertHash(HashTable *,ElemType);
//重建哈希表
void RecreateHashTable(HashTable *H)
{
int i,count=(*H).count;
ElemType *p,*elem=(ElemType*)malloc(count*sizeof(ElemType));
p=elem;
printf("recreate the hashtable\n");
for(i=0;i<m;i++)
if((*H).elem[i]!=NULLKEY)
{
*p=*((*H).elem+i);
p++;
}
(*H).count=0;
(*H).sizeindex++;
m=hashsize[(*H).sizeindex];
p=(ElemType*)realloc((*H).elem,m*sizeof(ElemType));
if(!p)
exit(0);
(*H).elem=p;
for(i=0;i<m;i++)
(*H).elem[i]=NULLKEY;
for(p=elem;p<elem+count;p++)
InsertHash(H,*p);
}
//若冲突次数过多,则重建哈希表
int InsertHash(HashTable *H,ElemType e)
{
int c,p;
c=0;
if(SearchHash(*H,e,&p,&c))
return DUPLICATE;
else if(c<hashsize[(*H).sizeindex]/2)
{
(*H).elem[p]=e;
++(*H).count;
return 1;
}
else
{
RecreateHashTable(H);
InsertHash(H,e);
}
return 0;
}
//按哈希地址的顺序遍历哈希表
void TraverseHash(HashTable H)
{
int i;
printf("hash address:0~%d\n",m-1);
for(i=0;i<m;i++)
if(H.elem[i]!=NULLKEY)
printf("address=%d key=%d\n",i,H.elem[i]);
}
//查找关键码为K的元素
int Find(HashTable H,ElemType K,int *p)
{
int c=0;
*p=Hash(K);
while((H.elem[*p]!=NULLKEY) && (H.elem[*p]!=K))
{
c++;
if(c<m)
collision(p,c);
else
return UNSUCCESS;
}
if (K == H.elem[*p])
return SUCCESS;
else
return UNSUCCESS;
}
int main()
{
ElemType r[N] = {17,60,29,38,1,2,3,4,60,13};
HashTable h;
int i, j, p;
ElemType k;
InitHashTable(&h);
for(i=0;i<N-1;i++)
{
j=InsertHash(&h,r[i]);
if(j==DUPLICATE)
printf("There has record %d,you cannot insert again.\n",r[i]);
}
printf("acorrd to the order of address ,the Hash table is: \n");
TraverseHash(h);
printf("search number:");
scanf("%d",&k);
j=Find(h,k,&p);
if(j==SUCCESS)
printf("address=%d key=%d\n",p,h.elem[p]);
else
printf("cannot find\n");
printf("insert number:");
scanf("%d",&k);
j=InsertHash(&h,k);
if(j==DUPLICATE)
printf("There has record %d,you cannot insert again.\n",k);
printf("the hashtable of the address order is:\n");
TraverseHash(h);
DestroyHashTable(&h);
return 0;
}
链地址法程序实现:
#define MAX_NUM 100 /*最大数据个数*/
#define PRIME 11 /*MAX_NUM的质数*/
#define NOTEXISTED NULL
/*定义数据结构*/
typedef struct Person
{
long id;
char name[21];
struct Person *link;
} Student;
/*建立哈希表*/
Student *Hashtab[MAX_NUM];
/*函数原型声明*/
long hashfun(long);
void insert();
void del();
Student *search(Student *,Student *);
void query();
void show();
void main()
{
char *menu_prompt=
"=== Hashing Table Program==\n"
" 1. Insert\n"
" 2. Delete\n"
" 3. Show\n"
" 4. Search\n"
" 5. Quit\n"
"Please input a number: ";
char menusele;
int i;
/*起始哈希表,将各链表指向NULL*/
for(i=0;i<MAX_NUM;i++)
Hashtab[i] = NULL;
do
{
printf("%s",menu_prompt);
menusele=toupper(getche());
puts("");
switch (menusele)
{
case '1' :
insert();
break;
case '2' :
del();
break;
case '3' :
show();
break;
case '4' :
query();
break;
case '5' :
puts("Bye Bye ^_^");
break;
default :
puts("Invalid choice !!");
}
} while(menusele !='5');
}
/*哈希函数:*/
/*以除法运算求出记录应存储的位置*/
long hashfun(long key)
{
return(key % PRIME);
}
void insert()
{
Student *newnode;
long index;
/*输入记录*/
newnode=(Student *)malloc(sizeof(Student));
newnode->link=NULL;
printf("Enter id : ");
scanf("%ld",&newnode->id);
printf("Enter Name : ");
scanf("%s",newnode->name);
/*利用哈希函数求得记录地址*/
index=hashfun(newnode->id);
/*判断该链表是否为空,若为空则建立链接表*/
if(Hashtab[index]==NULL )
{
Hashtab[index]=newnode;
printf("Node insert ok!\n");
}
else
{
/*查找节点是否已存在表中,如未存在,则将此节点插入表前端*/
if((search(Hashtab[index],newnode))==NOTEXISTED)
{
newnode->link=Hashtab[index];
Hashtab[index]=newnode;
printf("Node insert ok!\n");
}
else
printf("Record existed...\n");
}
}
/*删除节点函数*/
void del()
{
Student *node,*node_parent;
long index;
node=(Student *)malloc(sizeof(Student));
printf("Enter ID : ");
scanf("%ld",&node->id);
/*利用哈希函数转换记录地址*/
index=hashfun(node->id);
/*搜索节点是否存在,并返回指向该节点的指针*/
node=search(Hashtab[index],node);
if(node==NOTEXISTED)
printf("Record not existed...\n");
else
{
/*如节点为表首,则将表指向NULL,否则找到其父节点,并将父节点link指向节点后端*/
printf("ID : %ld Name : %s\n",node->id,node->name);
printf("Deleting record...\n");
if(node==Hashtab[index])
Hashtab[index]=NULL;
else
{
node_parent=Hashtab[index];
while(node_parent->link->id !=node->id)
node_parent=node_parent->link;
node_parent->link=node->link;
}
free(node);
}
}
/*查找节点函数,如找到节点则返回指向该节点的指针,否则返回NULL*/
Student *search(Student *linklist,Student *Node)
{
Student *ptr=linklist;
while (ptr->id !=Node->id && ptr->link !=NULL)
ptr=ptr->link;
if(ptr->link == NULL)
return NOTEXISTED;
else
return ptr;
}
/*查询节点函数*/
void query()
{
Student *query_node;
long index;
query_node=(Student *)malloc(sizeof(Student));
printf("Enter ID : ");
scanf("%ld",&query_node->id);
index=hashfun(query_node->id);
/*搜索节点*/
query_node=search(Hashtab[index],query_node);
if(query_node==NOTEXISTED )
printf("Record not existed...\n");
else
{
printf("ID : %ld Name : %s\n",
query_node->id,query_node->name);
}
}
/*显示节点函数,从哈希表一一寻找是否有节点存在*/
void show()
{
int i;
Student *ptr;
puts("ID NAME");
puts("------------------------");
for(i=0;i<MAX_NUM;i++)
{
/*表不为空,则显示整张表*/
if(Hashtab[i] !=NULL)
{
ptr=Hashtab[i];
while(ptr)
{
printf("%-5ld %15s\n",ptr->id,ptr->name);
ptr=ptr->link;
}
}
}
}
哈希表
最新推荐文章于 2023-03-29 18:23:13 发布
336

被折叠的 条评论
为什么被折叠?



