最近老师出一个题目:分析一个文本(英文文章)(300k—500k)中的词出现的频率,并且把频率最高的10个词打印出来。
要分析每个词的频率,肯定要先把每个单词读出来并存储起来,用数据库存肯定是没那个必要的,那么就输涉及怎么存储的问题。考虑到(300—500k)的文章,单词量不是像原来写程序中所读取的那样少,且读取以后要遍历多次进行单词的匹配,以便统计相同单词的个数,所以就要考虑一个效率的问题,每匹配一个词就要把所有的单词遍历一遍显然是效率不高的。在这里我用的方法是把长度相同的单词分在一块,遍历时就在相同长度的单词里找,这样就大大减小了匹配量,在一定程度上提升了效率。
下来是怎么实现的问题,首先排除链表,因为链表的一大缺点就是后期进行单词匹配的时候每次就要从头开始匹配,效率不高;结构体二维数组可以将不同的长度的单词分开,并且每个单元可以既可以存储单词本身,也可以存储一些单词本身的信息,但是C++中存储单词要用char类型的数组实现,个人认为这个东西不便操作,容易出错,所以就也不用此方法;最后只能用java,其中有String类型,可以将单词转化为字符串,个人认为对字符串操作比较容易,这样可以建立一个三维的数组,每一层上是相同长度的单词,每一层上的第一列式该单词的数量(匹配的时候,相同单词的第一个为),第二列是单词本身。
接下来就是具体的过程,首先就是遍历一次文章确定单词的总个数以及最长的单词的长度(用于确定三维数组需要多少层),接下来就是在遍历一遍用一个二维的数组存储每一层上的单词的数量(用来后面的空间申请),第三遍就是把每一个单词存储到相应的层上去,下来就遍历相应三维数组,一层一层的进行遍历统计每个单词的个数,并且同时用二维的整数数组动态记录遍历到当前时的最长的10个单词的在三维数组的位置,最后根据数组记录的位置找到最长的单词。
还有就是怎样统计单词,我这里就读到字母开始计数,同时记录其后一个字符如果不为字母就为一个单词,当然其中会有问题就是单词缩写的问题,由于此题关键不在于此,我就不作区分了,还有就是如果匹配还要考虑大小写的问题,所以存储前,就要将其转化为小写,在存储。
由于此程序思路比较简单,个人感觉没必要用那么复杂的结构,就只用了一个主函数,全部写下来了(就是有点乱)。还有一个问题是最后的输出次数最多的10个单词的时候,System.out.println(words[a][b][1]); 语句只执行一次就不执行了,跳出了循环,但是去掉此语句后,就可以循环10次,实在搞不懂,但是本人已经测试,记录最多次数的数组所记录的位置都是准确的,并且单词都已经存储到了相应的三维数组的位置。求大神解决。
以上只是我这个菜鸟的一些看法,请各位大神多多指教。
代码如下:
/*分析一个文本(英文文章)(300k—500k)中的词出现的频率,并且把频率最高的10个词打印出来。*/
import java.io.*;
public class Account_num
{
public static void main(String args[])
{
int member[][],max=0,mid=0; //member记录前10个单词的位置
int i,j,k,m,n;
int all=0,num[],num1=0,num2[],b=0,a=0,tmp11,tmp22;//num记录每层上单词的个数
member=new int[10][3];
char tmp1,tmp2,temp[]; //tmp用于从文件读取的中介,temp用于暂时记录每个单词
temp=new char[20];
for(i=0;i<20;i++)
temp[i]='\0';
String words[][][];//words用于记录所有的单词;
try{
FileInputStream f1=new FileInputStream("E:/Software_eng/a.txt");
tmp11=f1.read();
tmp22=f1.read();
while(tmp11!=-1) //第一遍读取,用于计算出总单词的个数
{tmp1=(char)tmp11;
tmp2=(char)tmp22;
///
if(((tmp1>='a')&&(tmp1<='z'))||((tmp1>='A')&&(tmp1<='Z')))
{
mid++;
if((((tmp1>='a')&&(tmp1<='z'))||((tmp1>='A')&&(tmp1<='Z')))&&!(((tmp2>='a')&&(tmp2<='z'))||((tmp2>='A')&&(tmp2<='Z'))))
{
all++;
if(mid>max)
{
max=mid;
}
mid=0;
}
}
tmp11=tmp22;
tmp22=f1.read();
}
f1.close();}
catch(IOException ie){
System.out.println(ie);
}
num=new int[max];
num2=new int[max];
for(i=0;i<max;i++)
{
num2[i]=0;
num[i]=0;
}
words=new String[max][][];
try{
FileInputStream f2=new FileInputStream("E:/Software_eng/a.txt");
tmp11=f2.read();
tmp22=f2.read();
mid=0;
while(tmp11!=-1) //第二次读取用于计算每一层上的单词的个数
{
tmp1=(char)tmp11;
tmp2=(char)tmp22;
if(((tmp1>='a')&&(tmp1<='z'))||((tmp1>='A')&&(tmp1<='Z')))
{
mid++; //用于记录中间每个单词的长度
}
if((((tmp1>='a')&&(tmp1<='z'))||((tmp1>='A')&&(tmp1<='Z')))&&!(((tmp2>='a')&&(tmp2<='z'))||((tmp2>='A')&&(tmp2<='Z'))))
{
num[mid-1]++; //相应的记录每层的长度的一维数组加一
mid=0; //一个单词结束,记录长度的数置0
}
tmp11=tmp22;
tmp22=f2.read();
}
f2.close();
}
catch(IOException ie){
System.out.println(ie);
}
for(i=0;i<max;i++)
{ num1=num[i];
num2[i]=num[i]; //num2数组用于记录每层上的数组的个数,以便于后面的使用
words[i]=new String[num1][2];//对每一层上的二维数组进行空间的申请
}
for(i=0;i<max;i++)
{
for(j=0;j<num[i];j++)
{
words[i][j][0]="1"; //用于记录该单词的个数,
words[i][j][1]=null;
}
}
try{
FileInputStream f3=new FileInputStream("E:/Software_eng/a.txt");
tmp11=f3.read();
tmp22=f3.read();
mid=0;
//第三次读取用于吧所有的单词存储到申请好的空间里
while(tmp11!=-1)
{tmp1=(char)tmp11;
tmp2=(char)tmp22;
if((((tmp1>='a')&&(tmp1<='z'))||((tmp1>='A')&&(tmp1<='Z'))))
{
temp[mid]=tmp1; //用于暂时保存读到的单词
mid++;
}
if((((tmp1>='a')&&(tmp1<='z'))||((tmp1>='A')&&(tmp1<='Z')))&&!(((tmp2>='a')&&(tmp2<='z'))||((tmp2>='A')&&(tmp2<='Z'))))
{
num1=num[mid-1]-1; //
num[mid-1]=num1;
words[mid-1][num1][1]=new String(temp);//将读到的单词放入相应的层地相应位置
words[mid-1][num1][1]=words[mid-1][num1][1].toLowerCase();//将单词转化为小写,便于后面的匹配
for(;mid>0;mid--)
temp[mid-1]='\0';//将数组重新置为空
mid=0;
}
tmp11=tmp22;
tmp22=f3.read();
}
f3.close();
}
catch(IOException ie){
System.out.println(ie);
}
/
for(i=0;i<10;i++)
{
for(j=0;j<3;j++)
{member[i][j]=0;//将记录出现次数最多单词位置的数组全置为0,初始化。
}
}
int mid1=0;
for(i=0;i<max;i++)
{
for(k=0;k<num2[i];k++)
{mid=0;
if(!(words[i][k][0]=="0"))
{
for(j=k;j<num2[i];j++)
{
if(words[i][k][1].compareTo(words[i][j][1])==0)
{
mid++;
if(mid!=1) //如果遇到相等的单词就个数加一
words[i][j][0]=""+0+"";
}
}
words[i][k][0]=""+mid+"";
if(mid>member[9][2]) //如果当前单词的出现次数大于已储存的
{ //最小的单词次数,就提换掉最小的,
member[9][0]=i; //在此声明member数组是用于存储当前出现次数
member[9][1]=k; //最多的单词在三维数组中的位置。
member[9][2]=mid; //顺续是按从大到小的顺序排的
for(n=0;n<9;n++) //然后是循环找到当前出现次数在已记录的
{ //次数中的位置
if(member[9-n][2]>member[9-n-1][2])
{
m=member[9-n][0];
member[9-n][0]=member[9-n-1][0];
member[9-n-1][0]=m;
m=member[9-n][1];
member[9-n][1]=member[9-n-1][1];
member[9-n-1][1]=m;
m=member[9-n][2];
member[9-n][2]=member[9-n-1][2];
member[9-n-1][2]=m;
}
}
}
///
}
}
}
System.out.println(member[0][2]);
System.out.println("出现频率最高的10个单词为:");
for(i=0;i<10;i++)
{
a=member[i][0]; //根据记录的位置输出单词
b=member[i][1];
System.out.println(a);
System.out.println(b);
System.out.println(words[a][b][1]);
}
}
}