数据结构文章推荐: |
---|
顺序表和链表实现图书管理系统 |
指针如何赋值?关于指针的理解 |
深度优先搜索判断有向图路径是否存在 |
待更新 |
1、掌握数据结构课程的基本内容和方法。
2、掌握基于线性表、二叉排序树和散列表不同存储结构的查找算法。
3、掌握不同检索策略对应的平均查找长度ASL的计算方法,明确不同检索策略的时间性能差别。
4、掌握相关排序算法。
一篇英文文章存储在一个文本文件中,分别基于线性表、二叉排序树和散列表的不同存储结构,实现单词词频的的统计和单词的检索功能。同时计算不同检索策略下的ASL,通过比较ASL的大小,对不同检索策略的时间性能做出相应的比较分析(在课程设计报告中给出)。具体内容如下:
1、一篇包括标点符号的英文文章存储在文本文件InFile.txt中,假设文件中单词的个数最多不超过5000个。从该文件中读取英文单词,过滤掉所有的标点符号。
2、分别基于线性表、二叉排序树和散列表的不同存储结构,实现单词词频的统计和单词的检索功能。其中,线性表采用顺序表和链表两种不同的存储结构分别实现顺序查找,同时实现基于顺序表的折半查找;散列表分别实现基于开放地址法的散列查找和基于链地址法的散列查找。
3、不论采用哪种检索策略,实现的功能均相同。
(1)词频统计
当读取一个单词后,若该单词还未出现,则在适当的位置上添加该单词,将其词频计为1;若该单词已出现过,则将其词频加1. 统计结束后,将所有单词及其频率按照字典顺序写入文本文件中。其中,不同的检索策略分别写入6个不同的文件:
基于顺序表的顺序查找:OutFile1.txt
基于链表的顺序查找:OutFile2.txt
基于顺序表的折半查找:OutFile3.txt
基于二叉排序树的查找:OutFile4.txt
基于开放地址法的散列查找:OutFile5.txt
基于链地址法的散列查找:OutFile6.txt
注:如果实现方法正确,6个文件的内容应该一致。
(2)单词检索
输入一个单词,如果查找成功,则输出该单词对应的频率,同时输出查找成功的平均查找长度ASL和查找所花的时间。如果查找失败,则输出“查找失败”的提示。
1、词频统计检索系统构思如下:
(一)大体框架:mainView()方法中为主界面的代码书写,在mainView()方法中,包含了用户选择功能的算法书写以及对应功能的调用
(二)结构体:为实现不同词频统计和检索算法做好准备,其中写了SqList、LinkList、BiNode、HashTable、HashNode五种结构体分别对应顺序表、链表、排序二叉树、哈希表顺序存储、哈希表链式存储等五种结构体
(三)读取代码:分别用了readBySq()、readByLL()、readByTree()、readByHash()、readByHL()来实现顺序表读写文件、链表读写文件、排序二叉树读写文件、哈希表顺序结构读取文件、哈希表链式结构读取文件等五种功能
编译环境:Codeblocks下新建C++项目进行编写(个人习惯)
2、词频统计检索系统代码如下:
#include
#include
#include
#include
#include
#define MAX_STORE 5000
/* 顺序表存储结构 */
typedef struct SqList{
char word[30]; //用于存储单词,最长30字符
int count = 1; //存储当前单词次数
};
/* 链表存储结构 */
typedef struct linkNode{
char word[30]; //用于存储单词,最长30字符
int count; //存储当前单词次数
linkNode *next; //下一个结点指针
}*LinkList,linkNode;
/* 排序二叉树存储结构 */
typedef struct BiNode{
char word[30]; //用于存储单词,最长30字符
int count; //用于存储当前单词次数
BiNode *lchild,*rchild; //左右孩子指针
}*BiTree,BiNode;
/* 哈希表顺序存储结构 */
typedef struct HashTable{
char word[30]; //用于存储单词,最长30字符
int count = 0; //存储当前单词次数
};
/* 哈希表链式存储结构 */
typedef struct HashNode{
char word[30]; //用于存储单词,最长30字符
int count = 0; //存储当前单词次数
HashNode *next; //下一个结点指针
}*HashLink,HashNode;
/* 所有全局变量的声明 */
bool isLoad1 = false,isLoad2 = false,isLoad3 = false,isLoad4 = false;//变量与是否进行了文件读取
int n = 0,num = 0,ASL = -999;
bool flag_word = false;
char buf[MAX_STORE],temp_word[30]; //作为读取文件的缓冲区
FILE *filePath; //作为文件的指针
FILE *fileWrite; //作为写文件的指针
int len = 0,i = 0,j = 0,k = 0,x = 0,y = 0; //行字符个数
///1、顺序表的声明
SqList sqList[MAX_STORE];
///2、链表的声明
LinkList linkList = (LinkList)malloc(sizeof(linkNode)); //分配空间
///3、二叉树的声明
BiTree tree; //空树
///4、哈希表的顺序存储结构声明
HashTable hash[MAX_STORE];
///5、哈希表的链式存储结构声明
HashLink hashLink[30]; //以单词长度为基准
clock_t start,finish; //算法开始和结束时间
//功能一:顺序表读写词频 - 写入OutFile1.txt
void readBySq(){
if((filePath=fopen("InFile.txt","r"))==NULL){
perror("文件不存在或读取错误!");
exit(1);
}
while(fgets(buf,MAX_STORE,filePath)!=NULL){//逐行读取
len = strlen(buf); //获取长度
for(i=0;i<len+1;i++){
if(buf[i]>='a'&&buf[i]<='z'||buf[i]>='A'&&buf[i]<='Z'){
if(buf[i]>='A'&&buf[i]<='Z')buf[i] += 32; //转换小写
if(!flag_word)flag_word = true; //标识符转换
temp_word[j] = buf[i]; //临时单词变量赋值
j++; //当前单词长度++
}else{
if(flag_word){
flag_word=false;//赋值n++;
bool flag_equ=false; //等值标识符
for(x=0;x<n;x++)if(strcmp(temp_word,sqList[x].word)==0){flag_equ=true;sqList[x].count++;break;}
if(!flag_equ){for(k = 0;k<j;k++)sqList[n].word[k] = temp_word[k];n++;}
j = 0;
}memset(temp_word, 0, sizeof(temp_word));
}
}
}
SqList temp;
for(x=0;x<n;x++)//循环排序部分
for(y=0;y<n-x-1;y++){
if(strcmp(sqList[y].word,sqList[y+1].word)>0){
temp = sqList[y];
sqList[y] = sqList[y+1];
sqList[y+1] = temp;
}
}
fileWrite = fopen("OutFile1.txt","w");
for(x=0;x<n;x++)fprintf(fileWrite,"%s %d\n",sqList[x].word,sqList[x].count);
num = n;n = 0;
}
//功能二:链表读写词频 - 写入OutFile2.txt
void readByLL(){
linkList->next = NULL;
linkNode *p = linkList;linkNode *temp = p;
if((filePath=fopen("InFile.txt","r"))==NULL){
perror("文件不存在或读取错误!");
exit(1);
}
while(fgets(buf,MAX_STORE,filePath)!=NULL){//逐行读取
len = strlen(buf); //获取长度
for(i=0;i<len+1;i++){
if(buf[i]>='a'&&buf[i]<='z'||buf[i]>='A'&&buf[i]<='Z'){
if(buf[i]>='A'&&buf[i]<='Z')buf[i] += 32; //转换小写
if(!flag_word)flag_word = true; //标识符转换
temp_word[j] = buf[i]; //临时单词变量赋值
j++; //当前单词长度++
}else{
linkNode *node = (LinkList)malloc(sizeof(linkNode));
node->next = NULL;
if(flag_word){
flag_word=false;
bool flag_equ=false; //等值标识符
while(p){
//printf("%s",p->word);
if(strcmp(p->word,temp_word)==0){p->count++;flag_equ=true;p = linkList;break;}
temp = p;p = p->next;
}p = temp;
if(!flag_equ){strcpy(node->word,temp_word);node->count = 1;p->next = node;n++;}
j = 0;p = linkList->next;
}memset(temp_word, 0, sizeof(temp_word));
}
}
}
for(p=linkList->next;p!=NULL;p=p->next)
for(temp=p->next;temp!=NULL;temp=temp->next){
if(strcmp(p->word,temp->word)>0){
x = p->count;strcpy(temp_word,p->word);
p->count = temp->count;strcpy(p->word,temp->word);
temp->count = x;strcpy(temp->word,temp_word);
}
}memset(temp_word, 0, sizeof(temp_word));
fileWrite = fopen("OutFile2.txt","w");p=linkList->next;num = n;n = 0;
while(p){fprintf(fileWrite,"%s %d\n",p->word,p->count);p=p->next;}
}
//功能三:二叉排序树读写词频 - 写入OutFile3.txt
void readByTree(){
BiNode *p1,*p2; //声明两个结点指针
if((filePath=fopen("InFile.txt","r"))==NULL){
perror("文件不存在或读取错误!");
exit(1);
}
while(fgets(buf,MAX_STORE,filePath)!=NULL){//逐行读取
len = strlen(buf); //获取长度
for(i=0;i<len+1;i++){
if(buf[i]>='a'&&buf[i]<='z'||buf[i]>='A'&&buf[i]<='Z'){
if(buf[i]>='A'&&buf[i]<='Z')buf[i] += 32; //转换小写
if(!flag_word)flag_word = true; //标识符转换
temp_word[j] = buf[i]; //临时单词变量赋值
j++; //当前单词长度++
}else{
if(flag_word){
flag_word=false;
if(tree == NULL){//先将树根赋值
tree = (BiTree)malloc(sizeof(BiNode));
strcpy(tree->word,temp_word); //给树根赋值
tree->count = 1; //给树根存储的单词频率赋值
tree->lchild = NULL;tree->rchild = NULL; //给两个子树赋空
}else{//如果树根非空,遍历主体
p1 = tree; //树根指针
while(strcmp(temp_word,p1->word)!=0){
if(strcmp(temp_word,p1->word)<0&&p1->lchild!=NULL){
p1 = p1->lchild; //p1指向其左孩子
}
if(strcmp(temp_word,p1->word)>0&&p1->rchild!=NULL){
p1 = p1->rchild; //p1指向其右孩子
}
if(strcmp(temp_word,p1->word)<0&&p1->lchild==NULL){
p2 = (BiTree)malloc(sizeof(BiNode)); //创建一个p1的左孩子
p2 ->lchild = NULL;p2->rchild = NULL; //给两个子树赋空
strcpy(p2->word,temp_word); //赋值
p2->count = 1;
p1->lchild = p2;
break;
}
if(strcmp(temp_word,p1->word)>0&&p1->rchild==NULL){
p2 = (BiTree)malloc(sizeof(BiNode)); //创建一个p1的右孩子
p2 ->lchild = NULL;p2->rchild = NULL; //给两个子树赋空
strcpy(p2->word,temp_word); //赋值
p2->count = 1;
p1->rchild = p2;
break;
}
}if(strcmp(temp_word,p1->word)==0){p1->count++;n++;} //标识符为真
}
j = 0;p1 = tree; //复原
}memset(temp_word, 0, sizeof(temp_word));
}
}
}
BiNode *st[MAX_STORE];p1 = tree;int top = 0;
fileWrite = fopen("OutFile3.txt","w");
do{
while(p1){
if(top == MAX_STORE)exit(1);//溢出错误
st[top++] = p1;
p1 = p1->lchild;
}
if(top){
p1 = st[--top];
//printf("%s ",p1->word); //测试输出
fprintf(fileWrite,"%s %d\n",p1->word,p1->count);
p1 = p1->rchild;
}
}while(top||p1);num = n;n = 0;
}
//功能四:哈希表顺序读取词频
void readByHash(){
if((filePath=fopen("InFile.txt","r"))==NULL){
perror("文件不存在或读取错误!");
exit(1);
}
while(fgets(buf,MAX_STORE,filePath)!=NULL){//逐行读取
len = strlen(buf); //获取长度
for(i=0;i<len+1;i++){
if(buf[i]>='a'&&buf[i]<='z'||buf[i]>='A'&&buf[i]<='Z'){
if(buf[i]>='A'&&buf[i]<='Z')buf[i] += 32; //转换小写
if(!flag_word)flag_word = true; //标识符转换
temp_word[j] = buf[i]; //临时单词变量赋值
j++; //当前单词长度++
}else{
if(flag_word){
flag_word=false;//赋值n++;
bool flag_equ=false; //等值标识符
y = 0;
for(x=0;x<j;x++){y += temp_word[x];}
while(hash[y%MAX_STORE].count!=0&&strcmp(hash[y%MAX_STORE].word,temp_word)!=0){y++;if(y>=MAX_STORE)exit(1);}
if(strcmp(hash[y%MAX_STORE].word,temp_word)==0){hash[y%MAX_STORE].count++;flag_equ=true;}
if(!flag_equ){strcpy(hash[y%MAX_STORE].word,temp_word);hash[y%MAX_STORE].count=1;n++;}
j = 0;
}memset(temp_word, 0, sizeof(temp_word));
}
}
}num = n;n=0;
}
//功能五:哈希表链式读取词频
void readByHL(){
HashNode *p = (HashLink)malloc(sizeof(HashNode));
for(i = 0;i<30;i++){hashLink[i]=(HashLink)malloc(sizeof(HashNode));hashLink[i]->count=0;hashLink[i]->next=NULL;}//初始化
if((filePath=fopen("InFile.txt","r"))==NULL){
perror("文件不存在或读取错误!");
exit(1);
}
while(fgets(buf,MAX_STORE,filePath)!=NULL){//逐行读取
len = strlen(buf); //获取长度
for(i=0;i<len+1;i++){
if(buf[i]>='a'&&buf[i]<='z'||buf[i]>='A'&&buf[i]<='Z'){
if(buf[i]>='A'&&buf[i]<='Z')buf[i] += 32; //转换小写
if(!flag_word)flag_word = true; //标识符转换
temp_word[j] = buf[i]; //临时单词变量赋值
j++; //当前单词长度++
}else{
if(flag_word){
flag_word=false;
bool flag_equ=false; //等值标识符
y = strlen(temp_word); //获取单词长度
HashNode *q = hashLink[y%30];
while(strcmp(q->word,temp_word)!=0&&q->next){q=q->next;}
if(strcmp(q->word,temp_word)==0){q->count++;flag_equ = true;}
if(!flag_equ){p->count=1;strcpy(p->word,temp_word);p->next=NULL;q->next=p;}
j = 0;p = (HashLink)malloc(sizeof(HashNode));
}memset(temp_word, 0, sizeof(temp_word));
}
}
}
}
//系统界面
void mainView(){
linkNode *p = linkList->next;
HashNode *q;
BiNode *p1,*p2; //声明两个结点指针
float ASL = 0; //散列表专用的ASl - Σ( ̄。 ̄ノ)
int choice = -999; //用户的选择
char str[30]; //用户查找的单词
printf("%-30s********************单词检索系统********************\n","");
printf("%-34s1、顺序表读写词频");
printf("%-10s2、单链表读写词频\n");
printf("%-34s3、二叉树读写词频");
printf("%-10s4、散列表读取词频\n");
printf("%-34s5、查询单词及ASL");
printf("%-11s6、退出单词检索系统\n");
printf("%-34s系统->请输入功能:","");
scanf("%d",&choice);
switch(choice){
case 1:
if(isLoad1){printf("%-34s系统->已用顺序表读取,请勿重复读!\n%-34s","","");}
else{readBySq(); printf("%-34s系统->词频统计并写入成功!\n%-34s","","");}
isLoad1 = true;
system("pause");
system("CLS");
break;
case 2:
if(isLoad2){printf("%-34s系统->已用链表读取,请勿重复读!\n%-34s","","");}
else{readByLL(); printf("%-34s系统->词频统计并写入成功!\n%-34s","","");}
isLoad2 = true;
system("pause");
system("CLS");
break;
case 3:
if(isLoad3){printf("%-34s系统->已用二叉树读取,请勿重复读!\n%-34s","","");}
else{readByTree(); printf("%-34s系统->词频统计并写入成功!\n%-34s","","");}
isLoad3 = true;
system("pause");
system("CLS");
break;
case 4:
if(isLoad4){printf("%-34s系统->已用散列表读取,请勿重复读!\n%-34s","","");}
else{readByHash();readByHL();printf("%-34s系统->词频统计并写入成功!\n%-34s","","");}
isLoad4 = true;
system("pause");
system("CLS");
break;
case 5:
if(!isLoad1&&!isLoad2&&!isLoad3&&!isLoad4){printf("%-34s系统->您还未进行文件读取!\n%-34s","","");system("pause");system("CLS");}
else{
printf("%-30s====================================================\n","");
printf("%-34s1、顺序表顺序查找","");
printf("%-10s2、单链表顺序查找\n","");
printf("%-34s3、顺序表折半查找","");
printf("%-10s4、二叉排序树查找\n","");
printf("%-34s5、开放地址法查找","");
printf("%-10s6、链地址法查找\n","");
printf("%-34s7、返回主界面","");
printf("%-14s系统->请输入功能:","");
scanf("%d",&choice); //输入选择
switch(choice){
case 1:
if(!isLoad1){printf("%-34s系统->请先用相应方法读取!\n%-34s","","");system("pause");break;}
printf("%-30s-------------------------------------------------------\n","");
printf("%-34s系统->请输入要查找的单词:","");
scanf("%s",&str);
start = clock();
for(x=0;x<num;x++){
if(strcmp(str,sqList[x].word)==0){finish = clock();printf("%-34s系统->单词(%s)出现的次数为:%d次 ASL=(%d/%d) 查找所花时间:%.3f秒\n","",sqList[x].word,sqList[x].count,(num+1),2,(double)(finish-start)/CLOCKS_PER_SEC);break;}
if(strcmp(str,sqList[x].word)!=0&&x==num-1){printf("%-34s系统->未查找到该单词!\n","");}
}printf("%-34s","");
system("pause");
break;
case 2:
if(!isLoad2){printf("%-34s系统->请先用相应方法读取!\n%-34s","","");system("pause");break;}
printf("%-30s-------------------------------------------------------\n","");
printf("%-34s系统->请输入要查找的单词:","");
scanf("%s",&str);
start = clock();
while(p){
if(strcmp(str,p->word)==0){finish = clock();printf("%-34s系统->单词(%s)出现的次数为:%d次 ASL=(%d/%d) 查找所花时间:%.3f秒\n","",p->word,p->count,(num+1),2,(double)(finish-start)/CLOCKS_PER_SEC);break;}
if(strcmp(str,p->word)!=0&&p->next==NULL){printf("%-34s系统->未查找到该单词!\n","");}p = p->next;
}printf("%-34s","");
system("pause");
break;
case 3:
if(!isLoad1){printf("%-34s系统->请先用相应方法读取!\n%-34s","","");system("pause");break;}
printf("%-30s-------------------------------------------------------\n","");
printf("%-34s系统->请输入要查找的单词:","");
scanf("%s",&str);
start = clock();
x = 0;y = num;k = (x+y)/2;
while(x<=y){
k = (x+y)/2;
if(strcmp(str,sqList[k].word)==0){
finish = clock();
printf("%-34s系统->单词(%s)出现的次数为:%d次 ASL=(%d/%d) 查找所花时间:%.3f秒\n","",sqList[k].word,sqList[k].count,(num+1),2,(double)(finish-start)/CLOCKS_PER_SEC);
break;
}else if(strcmp(str,sqList[k].word)>0){x=k+1;}
else if(strcmp(str,sqList[k].word)<0){y=k-1;}
}
if(strcmp(str,sqList[k].word)!=0){printf("%-34s系统->未查找到该单词!\n","");}printf("%-34s","");
system("pause");
break;
case 4:
if(!isLoad3){printf("%-34s系统->请先用相应方法读取!\n%-34s","","");system("pause");break;}
printf("%-30s-------------------------------------------------------\n","");
printf("%-34s系统->请输入要查找的单词:","");
scanf("%s",&str);
start = clock();
p1 = tree;
while(strcmp(str,p1->word)!=0&&p1){
if(strcmp(str,p1->word)>0){p1 = p1->rchild;}
if(strcmp(str,p1->word)<0){p1 = p1->lchild;}
}
if(strcmp(str,p1->word)==0){
finish = clock();
printf("%-34s系统->单词(%s)出现的次数为:%d次 ASL=(%.3f) 查找所花时间:%.3f秒\n","",p1->word,p1->count,log(num+1)/log(2),(double)(finish-start)/CLOCKS_PER_SEC);
}
if(strcmp(str,p1->word)!=0){printf("%-34s系统->未查找到该单词!\n","");}printf("%-34s","");
system("pause");
break;
case 5:
if(!isLoad4){printf("%-34s系统->请先用相应方法读取!\n%-34s","","");system("pause");break;}
printf("%-30s-------------------------------------------------------\n","");
printf("%-34s系统->请输入要查找的单词:","");
scanf("%s",&str);
start = clock();x = strlen(str);y = 0;bool flag;
for(i=0;i<x;i++){
y+=str[i];
}
ASL = 0.5*(1+MAX_STORE/((MAX_STORE-num)*1.0));//开放地址法ASL计算
while(strcmp(hash[y%MAX_STORE].word,str)!=0){y++;len++;if(y>=MAX_STORE){printf("%-34s系统->未查找到该单词!\n","");break;}}
if(strcmp(hash[y%MAX_STORE].word,str)==0){finish = clock();printf("%-34s系统->单词(%s)出现的次数为:%d次 ASL=(%.3f) 查找所花时间:%f秒\n","",hash[y%MAX_STORE].word,hash[y%MAX_STORE].count,ASL,(double)(finish-start)/CLOCKS_PER_SEC);}
printf("%-34s","");system("pause");
break;
case 6:
if(!isLoad4){printf("%-34s系统->请先用相应方法读取!\n%-34s","","");system("pause");break;}
printf("%-30s-------------------------------------------------------\n","");
printf("%-34s系统->请输入要查找的单词:","");
scanf("%s",&str);
start = clock();x = strlen(str);q = hashLink[x%30];
ASL = (1+num/(MAX_STORE*2.0));//链地址法ASL计算
while(strcmp(q->word,str)!=0&&q->next){q=q->next;len++;}
if(strcmp(q->word,str)==0){finish = clock();printf("%-34s系统->单词(%s)出现的次数为:%d次 ASL=(%.3f) 查找所花时间:%.3f秒\n","",q->word,q->count,ASL,(double)(finish-start)/CLOCKS_PER_SEC);}
if(strcmp(q->word,str)!=0){printf("%-34s系统->未查找到该单词!\n","");}
printf("%-34s","");system("pause");
break;
case 7:
break;
default:
printf("%-34s系统->输入有误!\n","");system("pause");
break;
}
}
system("CLS");
break;
case 6:
printf("%-34s系统->退出成功,欢迎下次使用!\n","");
exit(0);
default:
printf("%-34s系统->输入有误!\n","");system("pause");
break;
}
}
int main()
{
while(true)mainView();
return 0;
}
1、关于outfile文件是否要输出6个文件的问题,个人认为其实输出3个就够了,好比开放地址法,关键在于查找,而不在于遍历存储,纯个人理解。
2、完成该系统的过程中,基本都是先准备不同检索方法的结构体,其次再书写基于不同结构体的单词词频统计和检索方法。
3、书写该系统时,标识符flag的用处非常大,帮助我完成了许多功能的实现,在判断是否是单词或者下一个单词时,都起到了很大作用。
4、该课设代码中有些变量是我在写这个项目的时候用处不止一个的,所以可能不好理解,我也没有修改,请见谅。
5、这是在校期间的一个课设项目,在此做一个简单的记录与分享,如果有什么问题欢迎留言或私信我,谢谢支持!