利用hash技术统计C源程序中关键词的频度

一、问题描述

C语言关键词共37个(如main,if,for等)(不同教材可能不同)。用Hash表存储全部关键词,设Hash函数为:Hash(Key)=[(Key的首字母序号)*100+(Key的尾字母序号)] Mod 41。设计一个扫描C源程序,统计该程序中的关键词出现的次数。分别使用线性探测法和链地址法解决Hash冲突,比较分析两者的执行结果。

二、设计思路:

三、两种方法对比

1、线性探测法的优点是解决冲突简单,一个重大的缺点是容易产生堆积。这是由于当连续出现若干个同义词时(设第一个同义词占用单元d0,这连续的若干个同义词将占用Hash 表的d0,d0+1,d0+2等单元),随后任何d0+1,d0+2等单元上的哈希映射都会由于前面的同义词堆积而产生冲突,尽管随后的这些关键字并没有同义词。

2、与开放定址法相比,拉链法有如下几个优点: ①链地址法处理冲突简单,且无堆积现象,即非同义词决不会发生冲突,因此平均查找长度较短; ②由于链地址法中各链表上的结点空间是动态申请的,故它更适合于造表前无法确定表长的情况; ③开放定址法为减少冲突,要求装填因子α较小,故当结点规模较大时会浪费很多空间。而链地址法中可取α≥1,且结点较大时,链地址法中增加的指针域可忽略不计,因此节省空间; ④在用链地址法构造的散列表中,删除结点的操作易于实现。只要简单地删去链表上相应的结点即可。而对开放地址法构造的散列表,删除结点不能简单地将被删结 点的空间置为空,否则将截断在它之后填人散列表的同义词结点的查找路径。这是因为各种开放地址法中,空地址单元(即开放地址)都是查找失败的条件。因此在 用开放地址法处理冲突的散列表上执行删除操作,只能在被删结点上做删除标记,而不能真正删除结点。

链地址法的缺点: 指针需要额外的空间,故当结点规模较小时,开放定址法较为节省空间,而若将节省的指针空间用来扩大散列表的规模,可使装填因子变小,这又减少了开放定址法中的冲突,从而提高平均查找速度。

3、用链地址法解决冲突,更容易知道哪些关键词发生了冲突;而用线性探测法解决冲突容易知道该关键词是否发生冲突,但不易知道是哪些关键词发生了冲突。

四、代码

1、线性探测法

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define N 41
char key[37][10]={"auto","break","case","char",	"const","continue",
"default","do","double","else","enum","extern","float","for","goto",
"if","int","long","register","return","short","signed","sizeof","static",
"struct","switch","typedef","union","unsigned","void","volatile","while",
"inline","restrict","bool","true","false"};

typedef struct mykey{
	char data[10];
	int con;//冲突次数 
	int num;//出现次数 
}MYKEY;

mykey my[N];//hash表 
int sum=-1;                //总的关键字数 
FILE*fi;                   //所要读取文件指针 
FILE*fo;                   //所要写的文件指针 
void init();               //初始化结构体数组
int isChar(char a);        //判断字符是否是字母
void isKey(char str[]);    //判断是否是关键字
void myprint();            //打印Hash表
void test(int n);          //检测某文件
void choose(int n);        //选择读取的文件
bool readFile();           //读取文件
void find();               //查找某个关键词
void menu();               //显示操作菜单
void create();             //建立关键词Hash表

int main(){
	create();
	menu();
	return 0;
}
int isChar(char a){
	if(a>='a'&&a<='z'||a>='A'&&a<='Z'){
		return 1; 
	}else{
		return 0;
	}
}
void init(){
	fi=NULL;// 读取文件指针为空 
	sum=-1; //出现关键词总数置为-1 
	for(int i=0;i<N;i++){
		if(	my[i].num!=-1) {//hash表中该位置是否有关键词 
			my[i].num=0;
		}
	
	}
}

void isKey(char str[]){
	int n=strlen(str);
	int y=(str[0]*100+str[n-1])%N;
	bool isfind=false;
	int i=0;
	do{
		i++;
		if(strcmp(my[y].data,str)==0){//与hash表中关键词比较是否相同 
			sum++;//相同关键词总数加1 
			my[y].num++;//出现次数加1 
			isfind=true;
			break; 
		}else{
			y=(y+1)%N;
		}
	}while(i<=N);
}
void find(){
	char fin[10];
	bool flag=false; 
	do{
		printf("请正确输入要查找的关键词:\n");
		scanf("%s",fin);
		for(int i=0;i<N;i++){
			if(strcmp(key[i],fin)==0){
				flag=true;
				break;
			}
		}
	} while(flag==false);
	int n=strlen(fin);
	int y=(fin[0]*100+fin[n-1])%N;
	bool isfind=false;
	int i=0;
	do{
		i++;
		if(strcmp(my[y].data,fin)==0){
			isfind=true;
			break; 
		}else{
			y=(y+1)%N;
		}
	}while(i<=N);
	if(isfind==false){
		printf("很遗憾,该hash表内没有此关键词\n");
	}else{ 
		printf("hash表位置:%d  关键字:%s      出现次数:%d    冲突次数:%d\n",y,my[y].data,my[y].num,my[y].con);
	}
		int bian;
		printf("当前操作已完成\n");
		printf("接下来你可以做如下操作:\n");
		printf("0.退出,1.继续查询关键词,2.进行其它测试\n");
		do{
			printf("请输入正确的操作编号!\n");
			scanf("%d",&bian);
		}while(bian<0||bian>2);
		switch (bian){
			case 0:	printf("白白!");exit(0);break;
			case 1:find();break;
			case 2:init();menu();break;
		} 
} 
void myprint(){
	printf("关键词总数:%d\n",sum);
	fprintf(fo,"%s ","关键词总数:");
	fprintf(fo,"%d \n",sum);
	char s1[50]="hash表位置:";
	char s2[50]="无数据";
	char s3[50]="出现次数:";
	char s4[50]="冲突次数:";
	char s5[50]="关键字:";
	for(int i=0;i<N;i++){
		if(my[i].num==-1){
			printf("hash表位置:%d  无数据\n",i);
			fprintf(fo,"%s ",s1);
			fprintf(fo,"%d ",i);
			fprintf(fo,"%s \n",s2);
		} else{
			printf("hash表位置:%d  关键字:%s    冲突次数:%d      出现次数:%d\n",i,my[i].data,my[i].con,my[i].num);
			fprintf(fo,"%s ",s1);
			fprintf(fo,"%d ",i);
			fprintf(fo,"%s ",s5);
			fprintf(fo,"%s ",my[i].data);
			fprintf(fo,"%s ",s4);
			fprintf(fo,"%d \n",my[i].con);
			fprintf(fo,"%s ",s3);
			fprintf(fo,"%d ",my[i].num);
		}
	}
		int bian;
		printf("已打印hash表\n");
		printf("你可以做如下操作:\n");
		printf("0.退出,1.进行其它测试,2.查找某关键词\n");
		do{
			printf("请输入正确的操作编号!\n");
			scanf("%d",&bian);
		}while(bian<0||bian>2);
		switch (bian){
			case 0:	printf("白白!");exit(0);break;
			case 1:init();menu();break;
			case 2:find();break;
		}
}
bool readFile() {
	if(fi==NULL){
		printf("no file");
		return false; 
	}
	char ch;
	while(!feof(fi)){ 
		char word[10];
		int i=0;
		ch=fgetc(fi);
		while(isChar(ch)==0&&feof(fi)==0){
			ch=fgetc(fi); 
		}
		while(isChar(ch)==1&&feof(fi)==0){
			if(i==10){
				while(isChar(ch)==1&&feof(fi)==0){ 
					ch=fgetc(fi);
				}
				i=0;
				break;
			}else{
				word[i++]=ch;
				ch=fgetc(fi);
			}
		}
		word[i]='\0';
		isKey(word); //从源程序中读的单词是否是hash表中的关键词 
	}
	return true;
}

void menu(){
	int bian;
	printf("欢迎来到hash表系统首页!\n");
	printf("你可以选择如下操作:\n");
	printf("0.退出,1.测试集合交并补.cpp,2.测试980(A).cpp,3.测试1051(O).cpp,4.测试1051(S).cpp,5号测试\n");
	do{
		printf("请输入正确的操作编号!\n");
		scanf("%d",&bian);	
	}while(bian<0||bian>5);
	if(bian==0){
		printf("白白!");
		exit(0);
	}else if(bian>0&&bian<=5){
		test(bian);
	}
}
void test(int n){
	int bian;
	choose(n);
	if(readFile()){
		printf("文件读取成功,并已建立hash表\n");
		printf("你可以做如下操作:\n");
		printf("0.退出,1.打印hash表内容,2进行其它测试,3查找某关键词\n");
		do{
			printf("请输入正确的操作编号!\n");
			scanf("%d",&bian);
		}while(bian<0||bian>3);
		switch (bian){
			case 0:	printf("白白!");exit(0);break;
			case 1: myprint();break;
			case 2:init();menu();break;
			case 3:find();break; 
		}
	}else{
		printf("很遗憾,只能进行其它测试了!\n");
		printf("你有选择如下选择:\n");
		printf("0.退出,1.测试集合交并补.cpp,2.测试980(A).cpp,3.测试1051(O).cpp,4.测试1051(S).cpp\n");
		int bain; 
		do{
			printf("请输入正确的操作编号!\n");
			scanf("%d",&bian);	
		}while(bian<0||bian>4);
		if(bian==0){
			printf("白白!");
			exit(0);
		}else if(bian>0&&bian<=4){
			test(bian);
		}
	} 
}
void choose(int n){
	switch(n){
		case 1:fi=fopen("集合交并补.cpp","r");fo=fopen("集合交并补.txt","w");break;
		case 2:fi=fopen("980(A).cpp","r");fo=fopen("980(A).txt","w");break;
		case 3:fi=fopen("1051(O).cpp","r");fo=fopen("1051(O).txt","w");break;
		case 4:fi=fopen("1056(S).cpp","r");fo=fopen("1056(S).txt","w");break;
		case 5:fi=fopen("1051(S).cpp","r");break;
	}
	
}

void create(){
	for(int i=0;i<N;i++){
		my[i].num=-1;
		my[i].con=0;
	}
	for(int i=0;i<37;i++) {
		int n=strlen(key[i]);
		int y=(key[i][0]*100+key[i][n-1])%N;	//计算关键词hash值 
		if(my[y].num==-1) {//判断是否冲突 
			my[y].num++;
			strcpy(my[y].data,key[i]);
		}else{
				my[y].con++;
				int count=0,x;
				while(count<N){ //寻找下一个没有存放关键词的地方 
					y++;
					x=y%41;
					if(my[x].num==-1){//找到下一个没有存放关键词的地方 
						my[x].num++;
						strcpy(my[x].data,key[i]);
						break;
					}
					count++;
				}
		}	
	}	
} 

2、链地址法

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define N 41
char key[37][10]={"auto","break","case","char",	"const","continue",
"default","do","double","else","enum","extern","float","for","goto",
"if","int","long","register","return","short","signed","sizeof","static",
"struct","switch","typedef","union","unsigned","void","volatile","while",
"inline","restrict","bool","true","false"};

typedef struct mykey{
	char data[10];
	int con;//冲突次数 
	int num;//出现次数 
	mykey*next;//下一个节点 
}MYKEY;

mykey my[N];//hash表 
int sum=0;//总的关键字数 
FILE*fi;//所要读取文件指针 
FILE*fo;//所要写的文件指针 
void init(); 
int isChar(char a);
void isKey(char str[]);
void myprint(); 
void test(int n);
void choose(int n);
bool readFile();
void find();
void menu();
void create();
int main(){
	create();
	menu();
	return 0;
}
int isChar(char a){
	if(a>='a'&&a<='z'||a>='A'&&a<='Z'){
		return 1;
	}else{
		return 0;
	}
}
void init(){
	fi=NULL;
	sum=0; 
	mykey*p;
	for(int i=0;i<N;i++){
		p=my[i].next;
		while(p!=NULL){
			p->num=0;
			p=p->next;
		}
	}
}
void isKey(char str[]){
	int n=strlen(str);
	int y=((str[0]-96)*50+str[n-1]-96)%N;//计算hash值 找到头结点 
	mykey*p,*r;
	p=my[y].next;  
	while(p!=NULL){
		if(strcmp(p->data,str)==0){//比较两个单词是否相等来判断是否是关键词 
			sum++;
			p->num++;
			break;
		}
		p=p->next;
	}
}
void find(){
	char fin[10];
	bool flag=false;//标志查找关键词是否合法 
	do{
		printf("请正确输入要查找的关键词:\n");
		scanf("%s",fin);
		for(int i=0;i<N;i++){
			if(strcmp(key[i],fin)==0){
				flag=true;
				break;
			}
		}
	} while(flag==false);
	int n=strlen(fin); 
	int y=((fin[0]-96)*50+fin[n-1]-96)%N;//计算hash值 
	bool isfind=false; //是否找到关键词 
	mykey*p;
	p=my[y].next; 
	while(p!=NULL){ //遍历查找关键词 
		if(strcmp(p->data,fin)==0){
			isfind=true;
			break;
		}
		p=p->next;
	} 
	if(isfind==false){ 
		printf("很遗憾,该hash表内没有此关键词\n");
	}else{
		printf("hash表位置:%d  关键字:%s      出现次数:%d    冲突次数:%d\n",y,p->data,p->num,my[y].con);
	}
		int bian;
		printf("当前操作已完成\n");
		printf("接下来你可以做如下操作:\n");
		printf("0.退出,1.继续查询关键词,2.进行其它测试\n");
		do{
			printf("请输入正确的操作编号!\n");
			scanf("%d",&bian);
		}while(bian<0||bian>2);
		switch (bian){
			case 0:	printf("白白!");exit(0);break;
			case 1:find();break;
			case 2:init();menu();break;
		} 
} 
void myprint(){
	printf("关键词总数:%d\n",sum);
	fprintf(fo,"%s ","关键词总数:");
	fprintf(fo,"%d \n",sum);
	char s1[50]="hash表位置:";
	char s2[50]="无数据";
	char s3[50]="出现次数:";
	char s4[50]="冲突次数:";
	char s5[50]="关键字:";
	char s6[50]="其它数据如下:";
	mykey*p;
	for(int i=0;i<N;i++){
		p=my[i].next;
		if(p==NULL){
			printf("hash表位置:%d  无数据\n",i);
			fprintf(fo,"%s ",s1);
			fprintf(fo,"%d ",i);
			fprintf(fo,"%s \n",s2);
		}else{
			printf("hash表位置:%d    冲突次数:%d\n",i,my[i].con);
			fprintf(fo,"%s ",s1);
			fprintf(fo,"%d ",i);
			fprintf(fo,"%s ",s4);
			fprintf(fo,"%d \n",my[i].con);
			printf("其它数据如下:\n");
			fprintf(fo,"%s \n",s6);
			while(p!=NULL){
				printf(" 			  关键字:%s      出现次数:%d \n",p->data,p->num);
				fprintf(fo,"%s "," 	 ");
				fprintf(fo,"%s ",s5);
				fprintf(fo,"%s ",p->data);
				fprintf(fo,"%s ",s3);
				fprintf(fo,"%d \n",p->num);
				p=p->next;
			}
		}
	}
		int bian;
		printf("已打印hash表\n");
		printf("你可以做如下操作:\n");
		printf("0.退出,1.进行其它测试,2.查找某关键词\n");
		do{
			printf("请输入正确的操作编号!\n");
			scanf("%d",&bian);
		}while(bian<0||bian>2);
		switch (bian){
			case 0:	printf("白白!");exit(0);break;
			case 1:init();menu();break;
			case 2:find();break;
		}
}
bool readFile() {
	if(fi==NULL){
		printf("no file");
		return false;
	}
	char ch;
	while(!feof(fi)){
		char word[10];
		int i=0;
		ch=fgetc(fi);
		while(isChar(ch)==0&&feof(fi)==0){
			ch=fgetc(fi);
		}
		while(isChar(ch)==1&&feof(fi)==0){
			if(i==10){
				while(isChar(ch)==1&&feof(fi)==0){
					ch=fgetc(fi);
				}
				i=0;
				break;
			}else{
				word[i++]=ch;
				ch=fgetc(fi);
			}
		}
		word[i]='\0';
		isKey(word);
	}
	return true;
}

void menu(){
	init();
	int bian;
	printf("欢迎来到hash表系统首页!\n");
	printf("你可以选择如下操作:\n");
	printf("0.退出,1.测试集合交并补.cpp,2.测试980(A).cpp,3.测试1051(O).cpp,4.测试1051(S).cpp,5号测试\n");
	do{
		printf("请输入正确的操作编号!\n");
		scanf("%d",&bian);	
	}while(bian<0||bian>5);
	if(bian==0){
		printf("白白!");
		exit(0);
	}else if(bian>0&&bian<=5){
		test(bian);
	}
}
void test(int n){
	int bian;
	choose(n);
	if(readFile()){
		printf("文件读取成功,并已建立hash表\n");
		printf("你可以做如下操作:\n");
		printf("0.退出,1.打印hash表内容,2进行其它测试,3查找某关键词\n");
		do{
			printf("请输入正确的操作编号!\n");
			scanf("%d",&bian);
		}while(bian<0||bian>3);
		switch (bian){
			case 0:	printf("白白!");exit(0);break;
			case 1: myprint();break;
			case 2:init();menu();break;
			case 3:find();break; 
		}
	}else{
		printf("很遗憾,只能进行其它测试了!\n");
		printf("你有选择如下选择:\n");
		printf("0.退出,1.测试集合交并补.cpp,2.测试980(A).cpp,3.测试1051(O).cpp,4.测试1051(S).cpp\n");
		int bain; 
		do{
			printf("请输入正确的操作编号!\n");
			scanf("%d",&bian);	
		}while(bian<0||bian>4);
		if(bian==0){
			printf("白白!");
			exit(0);
		}else if(bian>0&&bian<=4){
			test(bian);
		}
	} 
}
void choose(int n){
	switch(n){
		case 1:fi=fopen("集合交并补.cpp","r");fo=fopen("集合交并补.txt","w");break;
		case 2:fi=fopen("980(A).cpp","r");fo=fopen("980(A).txt","w");break;
		case 3:fi=fopen("1051(O).cpp","r");fo=fopen("1051(O).txt","w");break;
		case 4:fi=fopen("1056(S).cpp","r");fo=fopen("1056(S).txt","w");break;
		case 5:fi=fopen("1051(S).cpp","r");break;
	}
	
}
void create(){
	for(int i=0;i<N;i++){//初始化 
		my[i].num=0;
		my[i].con=0;
		my[i].next=NULL;
	}
	mykey*p,*r;
	for(int i=0;i<37;i++) {
		int n=strlen(key[i]);
		int y=((key[i][0]-96)*50+key[i][n-1]-96)%N;//计算hash值 
		p=&my[y];
		if(p->next==NULL){ //是否冲突 
			r=(mykey*)malloc(sizeof(mykey));//采用尾插法建立链表
			strcpy(r->data,key[i]);
			r->num=0;
			p->next=r;
			r->next=NULL;	 
		}else{
			my[y].con++;
			while(p->next!=NULL){
				p=p->next;
			} 
			r=(mykey*)malloc(sizeof(mykey));
			strcpy(r->data,key[i]);
			r->num=0;
			p->next=r;
			r->next=NULL;
		}			 
	}	
} 

 

  • 8
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值