哈希表
哈希表主要考虑两个问题:1)如何设计哈希函数2)如何解决冲突
hash函数计算出元素在hash表中的储存位置:locate(i)=hash(keyi)hash表用于存储元素
第1节 hash函数构造方法
1)直接定址法:Hash(key)=a*Key+b优点:以关键码key的某个线性函数值为散列地址,不会产生冲突。缺点: 要占用连续地址空间,空间效率低。
2)除留余数法:Hash(Key)=Key % p (设表长未m,取p为小于等于m的质数)优点:节约空间缺点:会产生冲突
第2节 处理冲突的方法
1)开放地址法:Hi=(Hash(key)+di) % m当d为1,2,3……这个线性序列时,叫做线性探测法当d为1,2,3……的平方时,叫做二次序列当d也可以为伪随机数(我猜测这样的话可能需要用一张表事先记录伪随机序列,获取随机数时就从这张表来获取)
2)链地址法:将相同散列地址的元素组成一个链表,那么用数组是应该存储链表的元素还是地址呢?换言之,数组元素的存储结构应该是怎样的?
其它:再散列法(双散列函数)建立一个公共溢出区
一个结论:链地址法优于开发地址法。除留余数法作为散列函数优于其它类型函数。所以在本章节中,只学习和使用链式哈希表。
第3节 链式哈希表
哈希表的存储结构
// 链表结点
typedef struct ListNode
{
int data;
struct ListNode *next;
}ListNode;
typedef struct ListNode *List;
typedef struct HashTable
{
int tableSize;
List *table; // 优于List table[MAXSIZE];
}HashTable;
哈希表的算法实现
// 哈希表初始化,返回一个指向Hash表的指针
HashTable initHashTable(int size)
{
HashTable *H=malloc(sizeof(struct HashTable));
H->tableSize=size;
H->table=malloc(sizeof(List)*H->tableSize);
// 为每个链表增加一个虚拟头结点
for(int i=0;i<H->tableSize;i++)
{
H->table[i]=malloc(sizeof(struct ListNode));
if(H->table[i]==NULL)
{
return NULL:
}
H->table[i]->next=NULL:
}
return H;
}
// 哈希表查找
ListNode* find(int key,HashTable* H)
{
ListNode* p;
List L;
L=H->table[hash(key)];
p=L->next;
while(p!=null&&p->data!=key)
{
p=p->next;
}
return p;
}
// 哈希表插入
void insert(int key,HashTbale *H)
{
ListNode* pos;
pos=find(key,H);
if(pos==NULL)
{
ListNode *p=new ListNode;
L=H->table[hash(key)];
p->next=L->next;
p->data=key;
L-next=p;
}
}
注:如果哈希表的操作中不包含删除,那么就无需为链表添加头结点。但如果包含的话,最好还是使用头结点吧!
