BUAA数据结构第五次编程题——词频统计(二叉排序树BST实现)
看前须知
第五次上机题汇总
实验:树的构造与遍历——根据提示循序渐进(可惜提示有问题Ծ‸Ծ).
题目内容
问题描述
编写程序统计一个英文文本文件中每个单词的出现次数(词频统计),并将统计结果按单词字典序输出到屏幕上。
要求:程序应用二叉排序树(BST)来存储和统计读入的单词。
注:在此单词为仅由字母组成的字符序列。包含大写字母的单词应将大写字母转换为小写字母后统计。在生成二叉排序树不做平衡处理。
输入形式
打开当前目录下文件article.txt,从中读取英文单词进行词频统计。
输出形式
程序应首先输出二叉排序树中根节点、根节点的右节点及根节点的右节点的右节点上的单词(即root、root->right、root->right->right节点上的单词),单词中间有一个空格分隔,最后一个单词后没有空格,直接为回车(若单词个数不足三个,则按实际数目输出)。
程序将单词统计结果按单词字典序输出到屏幕上,每行输出一个单词及其出现次数,单词和其出现次数间由一个空格分隔,出现次数后无空格,直接为回车。
样例
当前目录下文件article.txt内容如下:
“Do not take to heart every thing you hear.”
“Do not spend all that you have.”
“Do not sleep as long as you want;”
输出
do not take
all 1
as 2
do 3
every 1
have 1
hear 1
heart 1
long 1
not 3
sleep 1
spend 1
take 1
that 1
thing 1
to 1
want 1
you 3
样例说明
程序首先在屏幕上输出程序中二叉排序树上根节点、根节点的右子节点及根节点的右子节点的右子节点上的单词,分别为do not take,然后按单词字典序依次输出单词及其出现次数。
题解
易错点和难点
易错点倒是没有,难点主要在于怎么构建BST,对于BST其实使用两种构建的形式,一种是通过传递struct TreeNode* 来构建,另一种是通过传递== struct TreeNode** ==来构建,对于两种构建形式,必然就会有谁快谁慢的问题,自己可以先尝试一下,比较比较。我在最后会告诉大家两者的具体构造形式和快慢比较。
参考代码
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<ctype.h>
#include<stdbool.h>
typedef struct TreeNode { //树的结构体
char val[200];
int times;
struct TreeNode *left;
struct TreeNode *right;
}Tree,*Treep;
void VISIT(Treep t); //访问树节点的内容
void inorder(Treep t); //中序遍历
void insertBST(Treep *t,char *num); //构造二叉排序树BST(这个方法是传递struct TreeNode**)
void GetArticle(FILE *fp1); //读取文章
void GetWord(); //获取单词
char s[2000000],tmp[200],ch;
int i,j,k;
Treep root=NULL;
int main()
{
FILE *fp1 = fopen("article.txt","r");
GetArticle(fp1);
GetWord();
if((root!=NULL) && (root->right!=NULL) && (root->right->right!=NULL)) //按照题目要求输出BST的内容
printf("%s %s %s\n",root->val,root->right->val,root->right->right->val);
else if((root!=NULL) && (root->right!=NULL))
printf("%s %s\n",root->val,root->right->val);
else if (root!=NULL)
printf("%s\n",root->val);
inorder(root);
return 0;
}
void VISIT(Treep t)
{
printf("%s %d\n",t->val,t->times); //访问单词和次数
}
void inorder(Treep t) //中序遍历
{
if(t!=NULL){
inorder(t->left);
VISIT(t);
inorder(t->right);
}
}
void insertBST(Treep *t,char *num)//构造BST
{
if(*t==NULL)//如果节点为空,构造节点
{
*t=(Treep)malloc(sizeof(Tree));
strcpy((*t)->val,num);
(*t)->times=1;
(*t)->left=NULL;
(*t)->right=NULL;
}
else if(strcmp((*t)->val,num)>0)//字典序排序
{
insertBST(&((*t)->left),num);
}
else if(strcmp((*t)->val,num)<0) //字典序排序
{
insertBST(&((*t)->right),num);
}
else if(strcmp((*t)->val,num)==0)//相同则次数增加
{
(*t)->times++;
}
}
void GetArticle(FILE *fp1) //读文章
{
ch=fgetc(fp1);
i=0;
while(ch!=EOF)
{
if(isalpha(ch)) s[i]=tolower(ch);
else s[i]=ch;
i++;
ch=fgetc(fp1);
}
}
void GetWord() //获取单词
{
for(i=0;i<strlen(s);i++)
{
if(isalpha(s[i])) //找到单词开头
{
for(j=i;j<strlen(s);j++)
{
if(!isalpha(s[j])) //找到单词结尾
{
break;
}
}
memset(tmp,'\0',sizeof(tmp));
for(k=0;k<j-i;k++)
{
tmp[k]=s[i+k]; //tmp存储的是单词;
}
insertBST(&root,tmp); //通过根节点构造BST
i=j;
}
else continue;
}
}
补充测试的数据
没有特别注意的地方,只要构造BST成功了,那么想不AC都难。
BST的两种构造形式及快慢分析
BST有两种构造形式
- 一种是传递struct TreeNode* (Treep)
Treep insertBST(Treep t,char *num) //构造BST
{
if(t==NULL)
{
t=(Treep)malloc(sizeof(Tree));
strcpy(t->val,num);
t->times=1;
t->left=NULL;
t->right=NULL;
}
else if(strcmp(t->val,num)>0)
{
t->left=insertBST(t->left,num);
}
else if(strcmp(t->val,num)<0)
{
t->right=insertBST(t->right,num);
}
else if(strcmp(t->val,num)==0)
{
t->times++;
}
}
- 一种是传递struct TreeNode** (Treep*)
void insertBST(Treep *t,char *num)
{
if(*t==NULL)
{
*t=(Treep)malloc(sizeof(Tree));
strcpy((*t)->val,num);
(*t)->times=1;
(*t)->left=NULL;
(*t)->right=NULL;
}
else if(strcmp((*t)->val,num)>0)
{
insertBST(&((*t)->left),num);
}
else if(strcmp((*t)->val,num)<0)
{
insertBST(&((*t)->right),num);
}
else if(strcmp((*t)->val,num)==0)
{
(*t)->times++;
}
}
对于两种构造形式,自己实操也不难发现,后者其实是更快的,因为在一般情况下,直接对地址进行操作是最快的。如果大家对此还没有什么概念,觉得两者都差不多,觉得记得麻烦只想记住一种,笔者建议记住后者,因为无论以后是大数据操作还是OJ卡运行时间,前者(传递Treep)会经常达不到要求,如果对此还是没有什么感觉可以移步题单链接,前者会让你体验到TLE的痛苦。。。。
题单链接
看看前者被TLE的题目对于时间的要求是多么严格(6ms噗)