散列表是一种用于查找的数据结构。其基本思想来自于索引,也可以看成是数组的一种扩展。对于一些数据信息,比如说图片文件名,如果我们要查找某张图片,通常将图片名作为关键字进行搜索。这个时候是不可能把图片名直接当成数组下标的,因此可以将图片名关键字通过某个函数映射为某个地址,或地址偏移量。那么每次要查找图片的时候直接输入关键字就能直接计算得出存储地址。其定义
T=h(k)
其中k为关键字,h为映射函数,T为得到的散列表,得到的函数值为地址或地址偏移量。
如果不同关键字通过某函数得到的散列值(地址偏移量)相同,这时就产生了冲突。解决冲突的方法一般有链接法和开放寻址法。
冲突发生后,链接法使冲突元素共享同一个散列值,并且用链表将这些冲突关键字串起来,一旦要查找时,先计算出散列值,然后在对应的链表中进行查找。开放寻址法解决理念则完全不一样。一旦冲突发生,开放寻址法则寻找散列表中没有被填充的槽,直到找到为止。根据散列函数的不同,又有线性散列,二次散列,双重散列等等。
下面是分离链接法的具体实现。参考自《数据结构与算法分析-C语言实现》。
头文件:
/*
* hash.h
* Created on: Dec 8, 2016
* Author: csf
*/
typedef int ElementType;
#ifndef HASH_H_
#define HASH_H_
#define MINTABLESIZE 5
#define MAXTABLESIZE 100
struct ListNode;
typedef struct ListNode *Position;
struct HashTbl;
typedef struct HashTbl *HashTable;
int IsPrime(int x);
int NextPrime(int y);
int Hash(ElementType key,int TableSize);
HashTable InitializeTable(int TableSize);
void DestroyTable(HashTable H);
Position Find(ElementType key,HashTable H);
void Insert(ElementType key,HashTable H);
ElementType Retrieve(Position P);
int Delete(ElementType key,HashTable H);
#endif /* HASH_H_ */
struct ListNode{
ElementType Element;
Position Next;
};
typedef Position List;
struct HashTbl{
int TableSize;
List *TheLists;
};
源文件:
/*
* hash.c
* Created on: Dec 8, 2016
* Author: csf
*/
#include "hash.h"
#include "stdio.h"
#include "stdlib.h"
int IsPrime(int x) //判断是否为素数
{
int i;
for(i=2;i*i<x;i++){
if(x%i==0)
return 0;
}
return 1;
}
int NextPrime(int y) //寻找最邻近素数
{
while(1){
if(IsPrime(y))
return y;
else
y++;
}
}
int Hash(ElementType key,int TableSize) //简单散列函数
{
return key%TableSize;
}
HashTable InitializeTable(int TableSize) //初始化散列表
{
HashTable H;
int i;
if(TableSize < MINTABLESIZE)
return NULL;
H=(struct HashTbl*)malloc(sizeof(struct HashTbl));
if(H==NULL)
return NULL;
H->TableSize=NextPrime(TableSize);
H->TheLists=malloc(sizeof(List)*H->TableSize);
if(H->TheLists==NULL)
return NULL;
for(i=0;i<H->TableSize;i++)
{
H->TheLists[i]=(struct ListNode*)malloc(sizeof(struct ListNode));
if(H->TheLists[i]==NULL)
return NULL;
else
H->TheLists[i]->Next=NULL;
}
return H;
}
Position Find(ElementType 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;
}
void Insert(ElementType key,HashTable H) //插入关键字
{
Position Pos,NewElement;
List L;
Pos=Find(key,H);
if(Pos==NULL)
{
NewElement=(struct ListNode*)malloc(sizeof(struct ListNode));
if(NewElement==NULL)
exit(0);
else
{
L=H->TheLists[Hash(key,H->TableSize)];
NewElement->Next=L->Next;
NewElement->Element=key;
L->Next=NewElement;
}
}
}
void DestroyTable(HashTable H) //销毁散列表
{
Position h,p,q;
int i;
for(i=0;i<H->TableSize;i++)
{
h=H->TheLists[i];
p=h->Next;
while(p)
{
q=p->Next;
if(!q)
{
free(p);
p=NULL;
}
else
{
free(p);
p=q;
}
}
}
}
int Delete(ElementType key,HashTable H) //删除关键字
{
Position Pos,L,Temp;
Pos=H->TheLists[Hash(key,H->TableSize)];
L=Pos;
while(L!=NULL && L->Next!=NULL && L->Next->Element!=key) //单链表删除比较麻烦,需要遍历,双链表则可借助Find
{
L=L->Next;
}
if(L==NULL)
return 0;
else if(L->Next==NULL)
free(L);
else
{
Temp=L->Next;
L->Next=L->Next->Next;
free(Temp);
}
return 1;
}
ElementType Retrieve(Position P) //定位
{
return P->Element;
}
void main() //测试程序
{
Position p;
HashTable table=InitializeTable(10);
Insert(0,table);
Insert(1,table);
Insert(81,table);
Insert(4,table);
Insert(64,table);
Insert(25,table);
Insert(16,table);
Insert(36,table);
Insert(9,table);
Insert(49,table);
p=Find(81,table);
if(p==NULL)
printf("can't find\n");
else
printf("find %d \n",p->Element);
if(Delete(81,table))
{
printf("Delete 81\n");
}
p=Find(81,table);
if(p==NULL)
printf("can't find 81\n");
else
printf("find %d \n",p->Element);
DestroyTable(table);
}