ALDS1_4_C:Dictionary 散列表(Hash表)实现

题目链接  http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ALDS1_4_C

输入:数量n 下面n行输入命令,insert "字符串"和find"字符串"

输出:对于insert命令建立一个散列表;对于find命令输出yes或者no,来标识表中是否有该字符串。

思路:所谓散列表,希望能对给定的输入key值,对应于表中的一个位置,输出这个key在散列表中存储的位置。有key到位置的固定的计算方法(函数),由人为规定。好处在于,位置直接和key值关联,只要知道key值可以瞬间定位到在表中存储的位置,插入和查找的复杂度都是O(1);

建立散列表的方法有两种,opening hash和closed hash,第一种是在数组后面挂链表的做法,我采取的是后面一种,开放地址法 open addressing

对于输入的一个字符串,本题只输入 A C G T 这四个字符组成的字符串,先把他们转换成对应四个数字,然后转换成对应的key值,我采取的是A-1 C-2 G-3 T-4 然后自己规定一个算法求出key值(当然,字符串对应的key值是惟一的,但是key值不能对应回唯一的字符串),我使用的方法如下

	for(ll i=0;i<len;i++){//len是字符串长度
		sum+=p*chtonum(s[i]);//chtonum是char to num字符串对应到数字,p初始为1,sum即是这个字符串对应的key值
		p*=5;
	}

对应到这个字符串的key值之后(好像也称之为字符串的hash值,典型的算法比如MD5,SHA1之类的,这样能基本上使不同的字符串对应不同的hash值,我采用的是最简单的算法,即很有可能不同字符串对应相同的key值),然后我希望能得到key在散列表中对应存储的位置h,我采用的函数是:h=(h1(key)+i*h2(key))%m;

其中

h1=key%m

h2=1+(key&(m-1))

这里的m常取的是散列表的范围,因为一个数模m,所得到的一定是一个在[0,m)之间的数,所以h得到的肯定是一个在散列表范围内的数,但是可能会存在不同字符串产生相同key值的情况,(即这个字符串的hash值对应的位置已经被别的元素占了)所以引入了i这个变量:随着i的取值变化,h会对应不同的位置;一般把i从0递增来取,这样会使得h的取值遍历整个散列表,直到找到一个可以存放的位置,或者无处安放。一般为了能让h的取值随着i的变化包含所有的散列表的范围,我们一般取一个与h2(k)互质的m,所有一般取一个比h2(k)大的质数。为了防止无法生成位置。试想,如果h2(k)能被m整除,则i的取值都没有任何意义了。

 

在查找的时候,只需要重复上述函数过程即可。

 

代码如下:

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;

#define row 1001333  //取一个比h2(k)大的质数
#define col 20

char H[row][col];

ll h1(ll key){
	return key%row;
}

ll h2(ll key){
	return 1+(key%(row-1));
}

ll chtonum(char s){  //character to number
	if(s=='A') return 1;
	if(s=='C') return 2;
	if(s=='G') return 3;
	if(s=='T') return 4;
	return 0;
}

ll get_key(char s[]){  //生成key值
	ll sum=0,p=1;
	int len=strlen(s);
	for(ll i=0;i<len;i++){
		sum+=p*chtonum(s[i]);
		p*=5;
	}
	return sum;
}

void insert(char s[]){
	ll key,h;
	key=get_key(s);
	ll i =0;
	while(1){
		h=(h1(key)+i*h2(key))%row;  //根据key值生成对应的位置
		if(strcmp(H[h],s)==0) return ;//这个元素字典里已经有了
		else if (strlen(H[h])==0){
			strcpy(H[h], s);//如果该位置没有别的值,就把这个元素插进去
			return ;
		} 
		i++;//如果这个i对应的h的位置已经有元素了,那么继续寻找下一个i对应的位置
	}
}

bool find(char s[]){
	ll key,h;
	key=get_key(s);
	ll i=0;
	while(1){
		h=(h1(key)+i*h2(key))%row;
		if(strcmp(H[h],s)==0) return true;//如果找到了该元素
		else if (strlen(H[h])==0) return false;
		i++;
	}
	return false;
}

int main (){
	int n;
	char c[20],s[20];
	for(int i = 0;i<row;i++) H[i][0]='\0'; //把数组初始化 方便比较
	scanf("%d",&n);
	for(int i =0;i<n;i++){
		cin>>c>>s;
		if(c[0]=='i') insert(s);
		else {
			if(find(s)) cout<<"yes"<<endl;
			else cout<<"no"<<endl;
		}
	}
	return 0;
}

 

错点:

 

1.find函数中一定要有 else if (strlen(H[h])==0) return false; 否则无法出循环

2.数据范围是long long

3.散列表第一个元素要初始化,否则不方便比较是否为空

4字符串读入过程中遇到玄学错误暂时没有找到错误,排错一个下午没找出错误,跑了无数次始终无法读入字符串,结果晚上11点多看完rng小组第一之后,啥也没改莫名其妙就正常了:D,恭喜RNG

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值