哈希表的创建

        在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使每个关键字和表中唯一的存储位置相对应,称这个对应关系f为哈希(散列)函数,根据这个思想建立的表为哈希表。
  若key1≠key2, 而f(key1)=f(key2), 则这种现象称为冲突,且key1和key2对哈希函数f来说是同义词。
  根据设定的哈希函数f=H(key)和处理冲突的方法,将一组关键字映像到一个有限的连续的地址集上,并以关键字在地址集中的"象"作为记录在表中的存储位置,这一映像过程,称为构造哈希表(散列表)。
  哈希函数常见的构造方法有5种:
  直接定址法、数字分析法、平方取中法、除留余数法、随机数法等。
  注意,在使用除留余数法 H(key)=key Mod p (p≤m)时,对除数p的选择很重要,若p选的不好,则很容易产生同义词。一般,将p选为素数。
  哈希函数处理冲突的方法常见的有2种:
  a) 开放地址法
  在该方法中,又细分为:线性探查法、平方探查法、伪随机数序列法、双哈希函数法等等。
  b)拉链法
  拉链法是把所有的同义词,用单链表链接起来的方法。在这种方法中,哈希表每个单元中存放的不再是记录本身,而是相应同义词单链表的头指针。
  哈希表的平均查找长度ASL = F(α),即哈希表的平均查找长度只与装填因子α有关,与哈希表的长度n无关。哈希表的时间复杂度一般为O(1)。
  例如,设计一个哈希表ha[0…m-1]存放n个元素,哈希函数采用除留余数法H(key)=key % p (p≤m), 解决冲突的方法采用开放定址法中的线性探查法。
  1) 设计哈希表的类型;
  2) 设计在哈希表中查找指定关键字的算法;
  3) 设计在哈希表中删除指定关键字的算法;
  4) 设计在哈希表中插入指定关键字的记录的算法;
  5) 设计一组关键字建立哈希表的算法;
  6) 设计输出哈希表的算法
  7) 求查找成功时,平均查找长度的算法;
  解:
   哈希表 ha[0…m-1]
   存放元素个数 n个
   哈希函数 H(key)= key % p (p≤m)
   线性探查法 H i ( k ) = ( H ( k ) + i )   m o d   m   , ( 1 ≤ i ≤ m − 1 ) H_i(k)=(H(k)+i) \, mod \,m \,,(1 \le i \le m-1) Hi(k)=(H(k)+i)modm,(1im1)
   1)设计哈希表的类型如下:

#define MaxSize 100      //定义最大哈希表长度
#define NULLKEY -1       //定义空关键字值
#define DELKEY -2        //定义被删关键字值
typedef int KeyType;     //关键字类型
typedef char *InfoType;  //其他数据类型
typedef struct{
	KeyType key;        //关键字域
	InfoType data;      //其他数据域
	int count;          //探查次数域
}HashTable[MaxSize];    //哈希表类型

        2)在哈希表中查找关键字为k的记录的过程:根据建表时设定的哈希函数H, 计算出哈希地址H(k), 若表中该地址单元不为空(关键字值NULLKEY表示为空)且该地址的关键字不等于k, 则按建表时设定的处理冲突的方法找下一个地址(这里采用线性探查法找下一个地址),如此反复下去,直到某个地址单位为空(查找失败,返回-1)或者关键字比较相等(查找成功,返回该地址)为止。对应的算法如下:

//在哈希表中查找关键字k
int SearchHT(HashTable ha,int p,KeyType k){
	int i=0,adr;
	adr = k%p;
	while (ha[adr].key!=NULLKEY && ha[adr].key!=k)
	{
		i++;             //采用线性探查法找下一个地址
		adr = (adr+1)%p;
	}
	if(ha[adr].key==k)  //查找成功
		return adr;
	else                //查找失败
		return -1;

}

        3) 在哈希表中删除关键字为k的记录的过程:采用开放定址法处理冲突的哈希表上执行删除操作,只能在被删记录上做删除标记,而不能真正删除记录。在这里设删除标记为DELKEY。对应的算法如下:

//删除哈希表中关键字k
int DeleteHT(HashTable ha,int p,int k,int &n){
	int adr;
	adr = SearchHT(ha,p,k);
	if (adr!=-1)   //在哈希表中找到关键字
	{
		ha[adr].key=DELKEY;
		n--;       //哈希表长度减1
		return 1;
	}
	else            //在哈希表中未找到该关键字
		return 0;

}

        4) 插入算法首先调用查找算法,若在表中找到待插入的关键字,则插入失败;若在表中找到一个开放地址,则将待插入的结点插入其中,即插入成功。对应的算法如下:

//将关键字k插入到哈希表中
void InsertHT(HashTable ha,int &n,KeyType k,int p){
	int i,adr;
	adr = k%p;
	if (ha[adr].key==NULLKEY || ha[adr].key==DELKEY)  //x[j]可以直接放在哈希表中
	{
		ha[adr].key=k;
		ha[adr].count=1;
	} 
	else      //发生冲突时,采用线性探查法解决冲突
	{
		i=1;  //i记录x[j]发生冲突的次数
		do 
		{
			adr = (adr+1)%p;
			i++;
		} while (ha[adr].key!=NULLKEY && ha[adr].key!=DELKEY);
		ha[adr].key=k;
		ha[adr].count=i;
	}
	n++;
}

        5) 建立哈希表的过程:先要将表中个结点的关键字清空,使其地址为开放的,然后调用插入算法将给定的关键字序列依次插入表中。对应的算法如下:

//创建哈希表
void CreateHT(HashTable ha,KeyType x[],int n,int m,int p){
	int i,n1=0;
	for (i=0;i<m;i++)  //哈希表置初值
	{
		ha[i].key=NULLKEY;
		ha[i].count=0;
	}
	for(i=0;i<n;i++)
		InsertHT(ha,n1,x[i],p);
}

        6) 输出一个哈希表的算法如下:

//输出哈希表
void DispHT(HashTable ha,int n,int m){
	float avg=0;
	int i;
	printf(" 哈希表地址:\t");
	for(i=0;i<m;i++)
		printf(" %3d",i);
	printf("\n");
	printf(" 哈希表关键字:\t");
	for(i=0;i<m;i++)
		if(ha[i].key==NULLKEY || ha[i].key==DELKEY)
			printf("   "); //输出3个空格
		else
			printf(" %3d",ha[i].key);
	printf(" \n");
	printf(" 搜索次数:\t");
	for(i=0;i<m;i++)
		if(ha[i].key==NULLKEY || ha[i].key==DELKEY)
			printf("   "); //输出3个空格
		else
			printf(" %3d",ha[i].count);
	printf("\n");
	for(i=0;i<m;i++)
		if(ha[i].key!=NULLKEY && ha[i].key!=DELKEY)
			avg=avg+ha[i].count;
	avg=avg/n;
	printf(" 平均搜索长度ASL(%d)=%.3g\n",n,avg);

}

        7) 求查找成功时的平均查找长度的算法如下:

//查找成功时,平均查找长度
void CompASL(HashTable ha,int m){
	int i;
	int s=0,n=0;
	for (i=0;i<m;i++)
		if(ha[i].key!=DELKEY && ha[i].key!=NULLKEY)
		{
			s=s+ha[i].count;
			n++;
		}
	printf(" 查找成功的ASL=%.3g\n",s*1.0/n);

}

        完整代码如下:

#include <stdio.h>
#include <stdlib.h>
#define MaxSize 100      //定义最大哈希表长度
#define NULLKEY -1       //定义空关键字值
#define DELKEY -2        //定义被删关键字值
typedef int KeyType;     //关键字类型
typedef char *InfoType;  //其他数据类型
typedef struct {
    KeyType key;        //关键字域
    InfoType data;      //其他数据域
    int count;          //探查次数域
}HashTable[MaxSize];    //哈希表类型

                        //将关键字k插入到哈希表中
void InsertHT(HashTable ha, int &n, KeyType k, int p) {
    int i, adr;
    adr = k%p;
    if (ha[adr].key == NULLKEY || ha[adr].key == DELKEY)  //x[j]可以直接放在哈希表中
    {
        ha[adr].key = k;
        ha[adr].count = 1;
    }
    else      //发生冲突时,采用线性探查法解决冲突
    {
        i = 1;  //i记录x[j]发生冲突的次数
        do
        {
            adr = (adr + 1) % p;
            i++;
        } while (ha[adr].key != NULLKEY && ha[adr].key != DELKEY);
        ha[adr].key = k;
        ha[adr].count = i;
    }
    n++;
}

//创建哈希表
void CreateHT(HashTable ha, KeyType x[], int n, int m, int p) {
    int i, n1 = 0;
    for (i = 0; i < m; i++)  //哈希表置初值
    {
        ha[i].key = NULLKEY;
        ha[i].count = 0;
    }
    for (i = 0; i < n; i++)
        InsertHT(ha, n1, x[i], p);
}

//在哈希表中查找关键字k
int SearchHT(HashTable ha, int p, KeyType k) {
    int i = 0, adr;
    adr = k%p;
    while (ha[adr].key != NULLKEY && ha[adr].key != k)
    {
        i++;             //采用线性探查法找下一个地址
        adr = (adr + 1) % p;
    }
    if (ha[adr].key == k)  //查找成功
        return adr;
    else                //查找失败
        return -1;

}

//删除哈希表中关键字k
int DeleteHT(HashTable ha, int p, int k, int &n) {
    int adr;
    adr = SearchHT(ha, p, k);
    if (adr != -1)   //在哈希表中找到关键字
    {
        ha[adr].key = DELKEY;
        n--;       //哈希表长度减1
        return 1;
    }
    else            //在哈希表中未找到该关键字
        return 0;

}

//输出哈希表
void DispHT(HashTable ha, int n, int m) {
    float avg = 0;
    int i;
    printf(" 哈希表地址:\t");
    for (i = 0; i < m; i++)
        printf(" %3d", i);
    printf("\n");
    printf(" 哈希表关键字:\t");
    for (i = 0; i < m; i++)
        if (ha[i].key == NULLKEY || ha[i].key == DELKEY)
            printf("   "); //输出3个空格
        else
            printf(" %3d", ha[i].key);
    printf(" \n");
    printf(" 搜索次数:\t");
    for (i = 0; i < m; i++)
        if (ha[i].key == NULLKEY || ha[i].key == DELKEY)
            printf("   "); //输出3个空格
        else
            printf(" %3d", ha[i].count);
    printf("\n");
    for (i = 0; i < m; i++)
        if (ha[i].key != NULLKEY && ha[i].key != DELKEY)
            avg = avg + ha[i].count;
    avg = avg / n;
    printf(" 平均搜索长度ASL(%d)=%.3g\n", n, avg);

}

//查找成功时,平均查找长度
void CompASL(HashTable ha, int m) {
    int i;
    int s = 0, n = 0;
    for (i = 0; i < m; i++)
        if (ha[i].key != DELKEY && ha[i].key != NULLKEY)
        {
            s = s + ha[i].count;
            n++;
        }
    printf(" 查找成功的ASL=%.3g\n", s*1.0 / n);

}


void main()
{
    int x[] = { 16,74,60,43,54,90,46,31,29,88,77 };
    int n = 11, m = 13, p = 13, i, k = 29;
    HashTable ha;
    CreateHT(ha, x, n, m, p);
    printf("\n"); DispHT(ha, n, m);
    i = SearchHT(ha, p, k);
    if (i != -1)
        printf(" ha[%d].key=%d\n", i, k);
    else
        printf(" 未找到%d\n", k);
    k = 77;
    printf(" 删除关键字%d\n", k);
    DeleteHT(ha, p, k, n);
    DispHT(ha, n, m);
    i = SearchHT(ha, p, k);
    if (i != -1)
        printf(" ha[%d].key=%d\n", i, k);
    else
        printf(" 未找到%d\n", k);
    printf(" 插入关键字%d\n", k);
    InsertHT(ha, n, k, p);
    DispHT(ha, n, m);
    printf("\n");

    system("pause");
}

        效果如下:

图(1) 哈希函数选为 H(key)= key % 13
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sanqima

一键三连,多多益善

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值