哈希表
散列表一般指哈希表,散列查找法又称杂凑法或散列法。
散列法通过对元素的关键字值进行某种运算,直接求出元素的地址,即使用关键字到地址的直接转换方法,而不需要反复比较。
哈希表的存储空间通常是一个连续数组,散列地址是数组的下标;通常,哈希函数是一个一对多的函数,所以冲突是不可避免的,只能通过选择一个最优的哈希函数来最大的减少冲突。
哈希函数并不唯一,一般按照实际情况构造哈希函数,主要有:
(1)数字分析法:当事先明确知道关键字每一位上各种数字的分布情况时适用
(2)平方取中法:当不能事先了解关键字的所有情况或难于直接从关键字中找到取值比较分散的几位时适用
(3)折叠法:当散列地址的位数较少而关键字的位数较多且难于直接从关键字中找到取值较分散的几位时适用
(4)除留余数法:最常用的构造哈希函数的方法,适用范围广
冲突处理
一、开放地址法
(1)线性探测法:将哈希表视作一个循环表,当发生冲突时从该地址往后找空单元进行放置,若表满则进行溢出处理
(2)平方探测法:当发生冲突时进行第k(k从1开始)次平方运算,加上当前地址i,找到空位i+k^2进行放置,若仍然冲突,则继续进行平方运算;查找时若发现该位置上与关键字不符,则进行k的平方运算加上当前地址i,直到查找到为止,若未查找到则表中不存在该关键字
//平方探测法
#include<stdio.h>
#define MAXSIZE 100
void HashTable(int a[],int key,int k)//假设哈希函数以%3作除留余数法
{
if(!a[key%3+k*k])
{
a[key%3+k*k]=key;
}
else
{
HashTable(a,key,k+1);
}
}
int HashSearch(int a[],int key,int k)
{
if(k*k>MAXSIZE)
{
return -1;
}
else if(key==a[key%3+k*k])
{
return key%3+k*k;
}
else
{
return HashSearch(a,key,k+1);
}
}
int main()
{
int a[MAXSIZE]={0},n,m,key;
printf("Please input the number of the elem:");
scanf("%d",&n);
while(n--)
{
int k=0;
scanf("%d",&m);
HashTable(a,m,k);
}
printf("Please input the number you want to search:");
scanf("%d",&key);
int i=HashSearch(a,key,0);
if(i==-1)
{
printf("No such elem!\n");
}
else
{
printf("The place of the key elem is:%d\n",i);
}
return 0;
}
(3)伪随机探测法:产生伪随机数,加上伪随机数取余计算下一个散列地址
处理冲突中发生的第一个散列地址不同的记录争夺后一个散列地址的现象称作 二次聚集或堆积
二、链地址法
又称拉链法:定义结构体数组,设置指针域,将具有相同散列地址的记录放在同一个单链表中,该单链表称为同义词链表。
实现代码
//拉链法
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 10
typedef struct LNode
{
int key;
struct LNode *next;
} LNode;
typedef struct HashTable
{
LNode *next;
} HashTable;
HashTable a[MAXSIZE];
void Init()
{
for (int i = 0; i < 10; i++)
{
a[i].next = NULL;
}
}
void CreateHashTable(int key)//建立哈希表
{
LNode *p, *q;
int i = key % 3;
if (a[i].next)
{
p = a[i].next;
while (p->next)
{
p = p->next;
}
q = malloc(sizeof(LNode));
q->key = key;
p->next = q;
q->next = NULL;
}
else
{
q = malloc(sizeof(LNode));
q->key = key;
a[i].next = q;
q->next = NULL;
}
}
LNode *HashSearch(HashTable a[], int key)//返回找到的关键字地址
{
LNode *p;
int i = key % 3;
if (a[i].next)
{
p = a[i].next;
while (p)
{
if (p->key == key)
{
break;
}
else
{
p = p->next;
}
}
return p;
}
else
{
return NULL;
}
}
int main()
{
LNode *k;
int n, m, key;
printf("Please input the node number of the HashTable:");
scanf("%d", &n);
while (n--)
{
scanf("%d", &m);
CreateHashTable(m);
}
printf("Please input the key number you want to research:");
scanf("%d", &key);
k = HashSearch(a, key);
if (k)
{
printf("The key number is:%d\n", k->key);
}
else
{
printf("No answer!\n");
}
return 0;
}
优点:
(1)无堆积,便于处理冲突
(2)哈希表插入删除操作易实现
(3)由于是动态申请内存,适合长度变化大的哈希表存储,节省空间
各方法处理散列表的ASL(平均查找长度)
实际情况中应根据具体情况选择最优的操作方法