当前位置:文档之家› 利用哈希技术统计C源程序关键字出现频度

利用哈希技术统计C源程序关键字出现频度

:利用哈希技术统计C源程序关键字出现频度目录一.需求分析说明 (3)二.总体设计 (3)三.详细设计 (4)四.实现部分 (5)五.程序测试 (10)六.总结 (11)一、需求分析说明1.课程设计目的本课程设计的目的就是要达到理论与实际应用相结合,使同学们能够根据数据对象的特性,学会数据组织的方法,能把现实世界中的实际问题在计算机内部表示出来,并培养基本的、良好的程序设计技能。

2.题目要求1)题目内容:利用Hash技术统计某个C源程序中的关键字出现的频度2)基本要求:扫描一个C源程序,用Hash表存储该程序中出现的关键字,并统计该程序中的关键字出现的频度。

用线性探测法解决Hash冲突。

设Hash函数为:Hash(key)[(key的第一个字母序号)*100+(key的最后一个字母序号)] MOD 41二、总体设计一.算法思想描述首先读取关键字文件以建立二叉排序树以供后续查询,每个树节点保存一个关键字字符串及指向左右子树的指针。

同时创建一Hash表,每个节点除应保存关键字字符串外,还应保存关键字频数及该存储单元冲突次数。

然后扫描一个C源程序,每次扫描一行,从中循环分离出每个单词,每次均查找其是否为关键字,若是,则按计算公式计算其KEY值并在Hash表中进行相应操作,若该节点为空则插入否者比较其是否与现有关键字相同,若相同则增加其频数,否则增加其冲突次数并继续线性探测下一个存储单元,完了继续操作下一个分离出来的单词,如此循环运行直至扫描结束。

编写本程序时,使用了二叉树创建、二叉树查找、Hash表的建立和操作及文件操作等基本算法。

二.三、详细设计三.(程序结构//Hash表存储结构typedef struct node //定义{ char s[20];int num,time; //num为频数,time为冲突次数}node;//二叉排序树结构定义typedef struct nod //定义{ char s[20];struct nod *left,*right;}nod;int max;//max为Hash表长度函数说明:nod *creat():读关键字文件,按照关键字中字符字母先后顺序建立二叉排序树,每个节点中保存一个关键字;void init(node *head):初始化Hash表各节点数据域;void deal(node *head,nod *parent,char filename[]):扫描源文件,分离出每个单词,检验是否为关键字;并根据检验结果来决定是否调用strdeal函数,以对Hash做适当更改;void strcp(node *head,char s[],int k):将新查找到的关键字复制到Hash表中第k个节点存储单元;void strdeal(node *head,char s[],int k):判断Hash表中第k个单元中有无关键字,若无则将当前关键字存入该单元,返回;否则比较两关键字是否相等,相等则将该单元频数加一,返回;不相等则将该单元冲突数加一并循环线性探测下一个存储单元;int strcmp(char t[],char s[]):字符串比较;void prin(nod *head):以左根右的顺序将二叉排序树打印在屏幕上;四、实现部分#include <iostream.h>#include <string>#include <iomanip.h>using namespace std;const int TOTAL=39; //39个关键字const int MAXLEN=10; //关键字长度const int HASHLEN=41; //哈希表长度int cont=0; //统计哈希表中的关键字个数void jiemian();void Show(int key);void Select(int choice);int Read(char *filename);int Input();int isLetter(char ch);int isKeyWords(char *word);int FindHX(char *keyword);int CreatHX(char *keyword);int GetFreePos(int key);void ResetHX();int GetKey(char *keyword);char KeyWords[TOTAL][MAXLEN]= //构造二维数组存储39个关键字{"asm","auto","break","case","cdecl","char","const","continue","default","do","double","else","enum","extern","far","float","for","goto","huge","if","int","interrupt","long","near","pascal","register","return","short","signed","sizeof","static","struct","switch","typedef","union","unsigned","void","volatile","while",};/******************************************************************** ***typedef struct HASH{char keyword[MAXLEN];int count; //出现次数(频度)int con; //冲突次数}HASH HS[HASHLEN];/******************************************************************** ********/class HASH //哈希表类{public:char keyword[MAXLEN];int count; //出现次数(频度)int con; //冲突次数};HASH HS[HASHLEN];int main(){ResetHX(); //先清空哈希表cout<<"\t=================================================== =============="<<endl;cout<<"\t* 欢迎使用该软件,请按提示操作*"<<endl;cout<<"\t* 该程序功能是统计一个文件中C语言关键字的频度*"<<endl;cout<<"\t* 统计开始前请先读取一个文件*"<<endl;cout<<"\t**"<<endl;cout<<"\t* by 黄耀广*"<<endl;cout<<"\t=================================================== =============="<<endl<<endl;jiemian();Select(Input());return(0);}void jiemian()//主菜单函数{cout<<endl;cout<<"\t\t1.读取一个文件"<<endl;cout<<"\t\t2.输出Hash表(关键字总数,冲突次数)"<<endl;cout<<"\t\t3.查询某关键字在Hash表中的情况"<<endl;cout<<"\t\t4.显示Hash表中的冲突关键字"<<endl;cout<<"\t\t5.显示C语言关键字的Hash函数值(作为对照)"<<endl;cout<<"\t\t6.回主菜单"<<endl;cout<<"\t\t7.退出"<<endl;}int Input(){cout<<endl;cout<<"按'6'回主菜单,请输入你的选择(1-7): ";while(true) //确保输入的为数字{int choice=0;if((cin>>choice)){if((choice<=0)||(choice>7))cout<<"输入范围不正确,请重新输入"<<endl;}else{cout<<"输入错误,请重新输入"<<endl;cin.clear();}while(!isspace(cin.get())) //功能:判断字符是否为空白符//说明:当字符为空白符时,返回非零值,否则返回零。

//空白符指空格、水平制表、垂直制表、换页、回车和换行符。

continue;cout<<endl;return choice;}}void Show(int key)//显示出某关键字的频度{if(key<0||key>=HASHLEN){cout<<"关键字不存在!"<<endl;return;}if(strlen(HS[key].keyword)==0){cout<<"哈希表位置:"<<key<<" 记录是空的"<<endl;return ;}cout<<"哈希表位置: "<<key<<" 关键字: "<<HS[key].keyword<<" 出现次数"<<HS[key].count<<endl;cont++;}void Select(int choice){char filename[128],word[MAXLEN];int i,key,count;switch(choice){case 1:cout<<"请输入要读取的文件名(文件必须与程序在同一目录下):";cin>>filename;cout<<endl;Read(filename);//read函数从一个文件读字节到一个指定的存储器区域,由长度参数确定要读的字节数Select(Input());break;case 2:cout<<"每次显示5行,请按回车键继续!"<<endl<<endl;for(i=0;i<HASHLEN;i++){Show(i);if((i+1)%5==0) getchar(); //为了清晰,每次显示5行}cout<<"关键字总数为:"<<cont<<endl;Select(Input());break;case 3:cout<<"请输入你想要查找的关键字:";cin>>word;cout<<endl;Show(FindHX(word));Select(Input());break;case 4:cout<<"\t冲突关键字列表"<<endl<<endl;count=0;for(i=0;i<HASHLEN;i++){if(strlen(HS[i].keyword)>0){key=GetKey(HS[i].keyword);if(key!=i){count++;cout<<"\t[关键字]: "<<HS[i].keyword<<endl;cout<<"\t[哈希表当前位置]: "<<i<<endl;cout<<"\t[冲突次数]: "<<HS[i].con<<endl<<endl;}}}if(count==0)cout<<"没有冲突"<<endl;elsecout<<"\t冲突关键字共:"<<count<<"个"<<endl;Select(Input());break;case 5:cout<<" C语言中的关键字和其在哈希表的位置:"<<endl;for(i=0;i<TOTAL;i++){cout<<setiosflags(ios::left)<<"["<<setw(2)<<GetKey(KeyWords[i])<<"]"<<setiosflags(ios::left)<<setw(11)<<KeyWords[i];if((i+1)%5==0) cout<<endl;}cout<<endl;Select(Input());break;case 6:jiemian();Select(Input());case 7:break;//退出default:Select(Input());return;}}int Read(char *filename) //读取文件{char word[MAXLEN],ch;int i;FILE *read;if( (read=fopen(filename,"r"))==NULL ) //只读方式打开一个文本文件,只允许读数据{cout<<"文件不存在,请确认好再输入!"<<endl;return -1;}ResetHX(); //读取文件前先清空哈希表while(!feof(read)) //feof()是文件结束检测函数,如果没有结束,返回值是0,结束了是1{i=0;ch=fgetc(read); //读取一个字符while(isLetter(ch)==0 && feof(read)==0 ) ch=fgetc(read);//如果不是字母的话接着读取while(isLetter(ch)==1 && feof(read)==0 ){if(i==MAXLEN){while(isLetter(ch)==1&& feof(read)==0){ch=fgetc(read);}i=0;break;}//超过关键字长度将跳过当前识别区域,读取后一单词else{ //将连续读取的字母存在数组里,组成一个单词word[i++]=ch;ch=fgetc(read);}}word[i]='\0'; //字符数组的结束标志if(isKeyWords(word)){CreatHX(word);}}fclose(read);cout<<"读取成功,文件中关键字已经存入hash表,请继续操作"<<endl;jiemian();return 1;}int isLetter(char ch) //判断是否是字母,因为关键字都是英文单词{if( (ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z') ) return 1;else return 0;//是字母就返回1,否则返回0值}int FindHX(char *keyword) //查找哈希表,分块查找{int key,find,tem=0;if(!isKeyWords(keyword)) return -1;key=GetKey(keyword);if(strcmp(HS[key].keyword,keyword)==0) return key;for(find=key+1;find<HASHLEN;find++){ //线性探查法顺序查找哈希表中是否已存在关键字tem++; //统计冲突次数if(strcmp(HS[find].keyword,keyword)==0){HS[find].con=tem;return find; }}for(find=0;find<key;find++){tem++;if(strcmp(HS[find].keyword,keyword)==0){HS[find].con=tem;return find; }}return -1;}int CreatHX(char *keyword) //建立哈希表{int key;if(!isKeyWords(keyword)) return -1;key=GetKey(keyword); //计算哈希值if(strlen(HS[key].keyword)>0) //判断关键字表中该位置是否存在关键字{ //已经存在有关键字if(strcmp(HS[key].keyword,keyword)==0){ //再判断哈希表中该位置的关键字是否相同HS[key].count++;return 1;}key=FindHX(keyword); //不相同,继续查找if(key<0){key=GetFreePos(GetKey(keyword));if(key<0) return -1;strcpy(HS[key].keyword,keyword); //将关键字插入哈希表}if(key<0) return -1;HS[key].count++;}else //该位置为空,直接将关键字插入该位置中{strcpy(HS[key].keyword,keyword);HS[key].count++;}return 1;}int GetFreePos(int key) //在哈希表中给关键字找个位置插进去{int find,tem=0;if(key<0||key>=HASHLEN) return -1;for(find=key+1;find<HASHLEN;find++) //先找后面的位置{tem++;if(strlen(HS[find].keyword)==0){HS[find].con=tem;return find; }}for(find=0;find<key;find++) //再找前面的位置{tem++;if(strlen(HS[find].keyword)==0){HS[find].con=tem;return find; }}return -1; //哈希表已满}void ResetHX() //重置哈希表,{int i;for(i=0;i<HASHLEN;i++){strcpy(HS[i].keyword,""); //不能用等号赋值HS[i].count=0;HS[i].con=0;}}int GetKey(char *keyword) //哈西函数{ //Hash函数为:Hash(Key)=[(Key的首字母序号)*100+(Key的尾字母序号)] Mod 41return ( keyword[0]*100+keyword[strlen(keyword)-1] ) % 41; //这里不要忘了减1}int isKeyWords(char *word) //判断是否关键字{int i;for(i=0;i<TOTAL;i++)if(strcmp(word,KeyWords[i])==0) return 1;return 0;}五.程序测试:文件一:HASH序号关键字频数冲突数3 return 8 04 long 3 05 typedef 1 06 goto 2 07 if 38 010 do 1 013 void 15 014 case 10 015 default 1 019 sizeof 1 021 int 29 122 switch 1 024 else 16 027 char 13 228 unsigned 2 033 struct 4 034 break 10 0总关键字数:164总冲突数:3文件二:HASH序号关键字频数冲突数0 while 11 03 return 130 04 long 26 06 goto 1 07 if 194 010 do 14 013 void 1 014 case 12 015 default 5 016 static 37 019 sizeof 12 021 interrupt 9 7022 int 69 123 for 75 224 else 68 225 far 1 126 switch 1 027 char 73 128 unsigned 1 034 break 20 2435 struct 22 236 short 2 0总关键字数:789总冲突数:127由以上数据可知,扫描不同的源文件,在模相同的情况下,其关键字经扫描后在Hash 表中存储的各项数据次序基本相同;并且,由程序运行结果可知Hash表空间开辟越大其冲突次数也并非一定减少。

相关主题