散列法的简单应用 | ALDS1_4_C: Dictionary | C实现

问题描述

请实现一个能执行以下命令的简易字典

insert str: 向字典中添加字符串str
find srt: 当前字典中包含str时输出yes,不包含时输出no

输入 第1行中输入命令数n。随后n行按顺序输入n个命令。命令格式如上。
输出 对于各find命令输出yes或no,每个输出占一行。
限制 输入的字符串仅由“A”,“C”,“G”, “T”四种字母构成。(脱氧核糖核酸)
1≤字符串长度≤12
n≤1000000

输入示例

6
insert AAA
insert AAA
find AAA
find CCC
insert CCC
find CCC

输出示例

yes
no
yes 

讲解

散列法是一种搜索算法,它可以根据各元素的值来确定存储位置,然后将位置保管在散列表中,从而实现数据的高效搜索。其中散列表是一种数据结构,能对包含关键字的数据集合高效地执行动态插入、搜索、删除操作。

散列表由容纳m个元素的数组T,以及根据数据关键字决定下标的函数共同组成。也就是说,我们要将数据的关键字输入该函数,由该函数决定数据在数组中的位置。散列表大概可以通过以下方法实现

insert(data)
	T[h(data.key)] = data
search(data)	
	return T[h(data.key)]

这里我们假设散列函数的输入值data.key为整数。请注意,当关键字为字符串等其他类型时,需要借助某些手段将其转换为相应的整数。

这里的h(k)时根据k值求数组T下标的函数,称为散列函数。另外,该函数的返回值称为散列值。散列函数求出的散列值范围在0到m - 1之间(m为数组T的长度)。为满足这一条件,函数内需要用取余运算,保证输出值为0到m - 1之间的整数。

比如h(k)=k mod m

这就是一种散列函数。不过,如果单有这一个运算,会发生不同key对应同一散列值的情况,即出现"冲突"。

开放地址法是解决这类冲突的常用手段之一。这里是双散列结构中使用的开放地址法如下,在双散列结构中一旦发生冲突,程序会调用第二个散列函数来求散列值

H(k)=h(k,i)=(h1(k)+i*h2(k)) mod m

散列函数h(k,i)拥有关键字k和整数i两个参数。这里的i是发生冲突后计算下一个散列值次数。也就是说,只要散列函数H(k)起了冲突,就会依次调用h(k,0), h(k,1), h(k,2)…直到不发生冲突为止,然后返回h(k,i)的值作为散列值。

要注意的是,因为下标每次移动h(k)个位置,所以必须保证T的长度m与h2(k)互质,否则会出现无法生成下标的情况。这种时候,我们可以特意让m为质数,然后取一个小于m的值作为h2(k),从而避免上述情况的发生

AC代码如下

#include<stdio.h>
#include<string.h>

#define M 1046527
#define NIL (-1)
#define L 14

char H[M][L];

//将字符转换为数值
int getChar(char ch){
	if(ch == 'A') return 1;
	else if(ch == 'C') return 2;
	else if(ch == 'G') return 3;
	else if(ch == 'T') return 4;
	else return 0;
} 

//将字符串转换为数值并生成key
long long getKey(char str[]){
	long long sum = 0, p = 1, i;
	for(i = 0; i < strlen(str); i++){
		sum += p*(getChar(str[i]));
		p *= 5;
	}
	return sum;
} 

int h1(int key){return key % M;}
int h2(int key){return 1 + (key % (M - 1));}

int find(char str[]){
	long long key, i, h;
	key = getKey(str);//将字符串转换为数值
	for(i = 0;;i++){
		h = (h1(key) + i * h2(key)) % M;
		if(strcmp(H[h], str) == 0) return 1;
		else if(strlen(H[h]) == 0) return 0;
	} 
	return 0;
}

int insert(char str[]) {
	long long key, i, h;
	key = getKey(str);//将字符串转换为数值
	for(i = 0; ; i++){
		h = (h1(key) + i * h2(key)) % M;
		if(strcmp(H[h], str) == 0) return 1;
		else if(strlen(H[h]) == 0){
			strcpy(H[h], str);
			return 0;
		}
	} 
}

int main(){
	int i, n, h;
	char str[L], com[9];
	for(i = 0; i < M; i++) H[i][0] = '\0';
	scanf("%d", &n);
	for(i = 0; i < n; i++){
		scanf("%s %s", com, str);//使用速度更快的输入函数scanf
		
		if(com[0] == 'i') {
			insert(str);
		} else {
			if(find(str) ){
				printf("yes\n");
			}else {
				printf("no\n");
			}
		}
	}
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值