数据结构课程设计:基于不同策略的英文单词的词频统计和检索系统

实习题目:    基于不同策略的英文单词的词频统计和检索系统             

实习环境:                                                 

实习内容:

一篇英文文章存储在一个文本文件中,然后分别基于线性表、二叉排序树和哈希表不同的存储结构,完成单词词频的统计和单词的检索功能。同时计算不同检索策略下的平均查找长度ASL,通过比较ASL的大小,对不同检索策略的时间性能做出相应的比较分析(比较分析要写在实习报告中的“收获和体会”中)。

实习要求

1. 读取一篇包括标点符号的英文文章(InFile.txt),假设文件中单词的个数最多不超过5000个。从文件中读取单词,过滤掉所有的标点。

2. 分别利用线性表(包括基于顺序表的顺序查找基于链表的顺序查找折半查找)、二叉排序树和哈希表(包括基于开放地址法的哈希查找、基于链地址法的哈希查找)总计6种不同的检索策略构建单词的存储结构。

3. 不论采取哪种检索策略,完成功能均相同。

(1)词频统计

当读取一个单词后,若该单词还未出现,则在适当的位置上添加该单词,将其词频计为1;若该单词已经出现过,则将其词频增加1。统计结束后,将所有单词及其频率按照词典顺序写入文本文件中。其中,不同的检索策略分别写入6个不同的文件。

基于顺序表的顺序查找--- OutFile1.txt

基于链表的顺序查找--- OutFile2.txt

折半查找--- OutFile3.txt

基于二叉排序树的查找--- OutFile4.txt

基于开放地址法的哈希查找--- OutFile5.txt

基于链地址法的哈希查找--- OutFile6.txt

注:如果实现方法正确,6个文件的内容应该是一致的。

(2)单词检索

输入一个单词,如果查找成功,则输出该单词对应的频率,同时输出查找成功的平均查找长度ASL和输出查找所花费的时间。如果查找失败,则输出“查找失败”的提示。

实习提示

不同的检索策略所采取的数据结构不一样,算法实现的过程不一样,但查找结果是一样的。

下面给出系统运行的部分参考截图。

主页面结果如图1所示。

 

图1

 

对于图1,选择1后进入“基于线性表的查找”,结果如图2所示。

 

图2

对于图2,选择1后进入“顺序查找”,结果如图3所示。

 

图3

 

对于图3,选择1后进入“基于顺序表的顺序查找”,结果如图4所示。

 

图4

 

对于图4,选择1后进入“词频统计”,完成词频统计功能。要求统计出所有单词的总数和每个单词的词频。统计结果全部输出到对应的文件OutFile1.txt中,该文件中的数据总计n+1 ,第一行n为所有单词的总数,后面n行为每个单词及其出现的频率(单词和频率用空格分隔)。

文件OutFile1.txt的示例如图5所示。

 

图5

 

对于图4,选择2后进入“单词查找”,输入待查找的单词后,如果查找成功,结果如图6所示。首先显示此单词的词频,之后分别给出查找该单词所花的时间(单位为毫秒或微秒或纳秒,可以利用高精度的时间函数做计算来实现)和平均查找长度。如果查找失败,结果如图7所示。

 

 

图6

 

 

图7

 

对于其他5种检索策略,查找结果同图6或图7所示,只是在查找成功时所花的时间和平均查找长度不同而已。

 

选做内容:

1. 用窗体版界面取代控制台界面。

2. 程序中所用的到排序算法选用先进的排序算法(快速排序或归并排序或堆排序)。

3. 实现低频词过滤功能,要求将出现频率(词频/单词总数)低于10%的单词删除,可以选择不同的存储结构分别实现。

(1)顺序表

(2)链表

(3)二叉排序树

(4)基于开放地址法的哈希表

(5)基于链地址法的哈希表

 

实习任务阶段性安排

周一:

  1. 明确实习任务和要求,理解相关算法实现方案
  2. 实现主要的界面及界面之间的跳转(力求满足界面友好性
  3. 实现基于顺序表的顺序查找(即完成图1--7的所有功能)

周二:

  1. 实现线性表、二叉排序树的所有功能

周三:

  1. 实现哈希表的所有功能

周四:

  1. 完善系统
  2. 对整个系统功能进行测试
  3. 改进程序(力求程序满足正确性、可读性、健壮性、高效性
#include<iostream>
#include<fstream>
#include<cstring>
#include<cmath>
#include <windows.h>
#include <time.h>
#include <iomanip> //要用到格式控制符
#define MAXSIZE 5000 
#define m 5000                        			//哈希表的表长
#define NULLKEY " "                 			//单元为空的标记

using namespace std; 

int t;//单词总数 
int factnum;//去重后单词数 
string str[MAXSIZE];//存放单词 
int fre[MAXSIZE];//存放词频
int flag[MAXSIZE];//标记第一次出现的位置 
double rate[MAXSIZE];
int setrate=100;//万分之100以下的单词过滤
int setrate2;
	
typedef struct{
	string key;
	int Time;
}Word; 

typedef struct{
	Word *R;
	int length;
}SSTable;

typedef struct LNode
{
	Word data;
	struct LNode *next;
}LNode,*LinkList;

typedef struct BSTNode
{
 	Word data;
 	struct BSTNode *lchild,*rchild;
}BSTNode,*BSTree;

BSTree T1;//

struct HashTable{
  	string  key;		         	 		
 	int  Time;				
};

typedef struct WordNode//单词结点 
{
	Word data;
	struct WordNode *nextarc;
}WordNode;
typedef struct LetterNode//首字母表头结点 
{
	int letter_no;//ch
	WordNode *firstarc;
}LetterNode,AdjList[38];//?

typedef struct
{
	AdjList Letter;//Letter
}ALGraph;//邻接表 

int Hash_int(int a)
{
	int p;
	p=str[a][0];
	p=p-96;
	return p;
}

void InitTree(BSTree &T)
{
	T=new BSTNode;
	T=NULL;
	return;
}

void Insert(BSTree &T,Word e)
{
	if(!T)
	{
			//建立一个根节点指针S并开辟存储空间 
			BSTree S;
			S=new BSTNode;
			//为新结点S赋值 
			S->data=e;
			//初始化孩子左右结点为空 
			S->lchild=S->rchild=NULL; 
			T=S; //S赋值给T 
	}
	else if(e.key<T->data.key)//如果待插入元素小于根节点,插入左边 
		Insert(T->lchild,e);
	else if(e.key>T->data.key)//大于,右边 
		Insert(T->rchild,e);
}

int Input3(BSTree &T,string key){
	int n=0;
	if(T==NULL)return -1;
    while(T!=NULL&&key!=T->data.key)//非递归遍历 
	{ 
		n++;
        if (key<T->data.key)
            T=T->lchild;
        else
            T=T->rchild;
    }
	if(T==NULL)return -1;                    
    if(key==T->data.key)
	{
		T->data.Time++;
		return n;
	}
}

int Search5(BSTree &T,string key){
	int n=0; 
	if(T==NULL)return -1;
    while(T!=NULL&&key!=T->data.key)
	{
		n++;
        if (key<T->data.key)
            T=T->lchild;
        else
            T=T->rchild;
    }
	if(T==NULL)return -1;                    
    if(key==T->data.key)
	{
		return n;
	}
}

void InputTree(BSTree &T)
{
	int i=1;
	//建立一颗空树 
	T=NULL;
	//建立一个word结点,先赋值,便于录入 
	Word e;
	e.key=str[1];
	e.Time=1;
	while(i<t)//单词数组未输入完 
	{
		//建立一个临时的树,用来遍历查重,这个遍历将改变树的根结点,是毁灭性的 
		BSTree temp;
		InitTree(temp);
		temp=T;
		//判断是否重复 
		int res=Input3(temp,str[i]); //在此统计词频,若重复频次就加1。
		
		if(res==-1)//如果不重复,就录入.
		{
			Insert(T,e);
		}
		//else T->data.Time++;//不行 
		
		//下一次的输入 
		i++;
		e.key=str[i];
		e.Time=1;//初次输入,词频都为1
	}
}

int TraverseOut(BSTree T)
{

	fstream out;
	out.open("OutFile3.txt",ios::app);
	if(T)
	{
		TraverseOut(T->lchild);
		//cout<<T->data.key<<" "<<T->data.Time<<endl;
		out<<T->data.key<<" "<<T->data.Time<<endl;
		TraverseOut(T->rchild); 
	}
	return 0; 
}

int TraverseOut2(BSTree T)
{
	if(T)
	{
		TraverseOut2(T->lchild);
		cout<<T->data.key<<" "<<T->data.Time<<endl;
		TraverseOut2(T->rchild); 
	}
	return 0; 
}

void Output3(BSTree T)
{
	int i;
	fstream out;
	out.open("OutFile3.txt",fstream::out | ios_base::trunc);
	out<<"单词 词频"<<endl; 
	TraverseOut(T);//遍历树的结点,依次输出 
	return;
}

void Search3(BSTree T)
{
	Output3(T);
	//TraverseOut2(T); 
	cout<<"基于二叉排序树,请输入你要查找的单词:" <<endl;
	string ch;
	cin>>ch;
	
	BSTree temp;
	InitTree(temp);
	temp=T;
	
	LARGE_INTEGER fr;
    LARGE_INTEGER st;
    LARGE_INTEGER ed;
    double time;
	QueryPerformanceFrequency(&fr);
    QueryPerformanceCounter(&st); 
	int res=Search5(temp,ch);
	QueryPerformanceCounter(&ed);
	time = (double)(ed.QuadPart - st.QuadPart) / (double)fr.QuadPart;

	if(res!=-1)
	{
			cout<<"此单词的词频为:"<<temp->data.Time<<endl;
			cout<<"查找所花费的次数:"<<res<<endl;	
			cout<<"查找所花费的时间:"<<setprecision(6)<<time*1000000<<"ns"<<endl;
			cout<<"平均查找长度:"<<((factnum+1)/factnum) * log2(factnum+1) - 1<<endl;
	}
	else cout<<"查找失败"<<endl; 
}

void Search3_1(BSTree T,string ch)
{
	Output3(T);
	cout<<"基于二叉排序树:" <<endl;
	
	BSTree temp;
	InitTree(temp);
	temp=T;
	
	LARGE_INTEGER fr;
    LARGE_INTEGER st;
    LARGE_INTEGER ed;
    double time;
	QueryPerformanceFrequency(&fr);
    QueryPerformanceCounter(&st); 
	int res=Search5(temp,ch);
	QueryPerformanceCounter(&ed);
	time = (double)(ed.QuadPart - st.QuadPart) / (double)fr.QuadPart;
	
	if(res!=-1)
	{
			cout<<"此单词的词频为:"<<temp->data.Time<<endl;
			cout<<"查找所花费的次数:"<<res<<endl;
			cout<<"查找所花费的时间:"<<setprecision(6)<<time*1000000<<"ns"<<endl;
			cout<<"平均查找长度:"<<((factnum+1)/factnum) * log2(factnum+1) - 1<<endl;
	}
	else cout<<"查找失败"<<endl; 
}

void InitList(SSTable &L)
{
	L.R=new Word[MAXSIZE];
	//if(!L.elem) return;
	L.length=0;
	return;
}

int CheckSqList(SSTable L,string a)
{
	for(int i=1;i<=L.length;i++)
		if(a==L.R[i].key) return i;//返回重复单词在数组中的位置 
	return 0;
}

void Input1(SSTable &L)
{
	//词频统计&存入内存 
	int i=1;
 	while(i<=t)
 	{ 	
		if((str[i][0]<'0'&&!CheckSqList(L,str[i]))||(str[i][0]>'9'&&!CheckSqList(L,str[i])))//不重复录入 
		{
				L.length++;
				L.R[L.length].key=str[i];  
				L.R[L.length].Time=1;//不能++ 
				//标记位置
				flag[i]=1; 
		}
		else if((str[i][0]<'0'&&CheckSqList(L,str[i]))||(str[i][0]>'9'&&CheckSqList(L,str[i])))//重复就只统计 
			{
				L.R[CheckSqList(L,str[i])].Time++;//词频统计  
			} 		
		i++;
	}	
}

void Rate1(SSTable L)
{
	fstream out;
	out.open("OutFile1.txt",fstream::out | ios_base::trunc);
	int i=0;
	cout<<"单词"<<" "<<"频次"<<" "<<"概率(万分之一)"<<endl;
	while(i<=factnum)
 	{ 	
		if(flag[i]==1)//如果是第一个元素,就输出 
		{
				fre[i]=L.R[i].Time; 
				rate[i]=(double)fre[i]/t;
				rate[i]=rate[i]*10000;
				if(rate[i]>setrate)//&&rate[i]<100000
				{
					out<<L.R[i].key<<" "<<fre[i]<<endl;
					cout<<"i:"<<i<<" "<<L.R[i].key<<" "<<fre[i]<<"/"<<t<<" "<<rate[i]<<endl;
				}		
		}
		i++;
	}	
}

int Locate(LinkList L,string a)//定位重复结点 
{

	LinkList p;
    p=L;  //p指向第一个结点
    int i=0;             
    while(p->next){//遍历单链表
           i++;
           p=p->next; 
		   if(p->data.key==a) 
		   {
		   		p->data.Time++;//有重复,频率加1
				// cout<<"有重复,位置为"<<i<<endl; 
		   		return i; 
		   }
		   
    }           
	return 0;
}

void Input2(LinkList &L)
{
	LinkList r,p;int i=0;
	L=new LNode;
	L->next=NULL;
	r=L;
	for(i=1;i<=t;i++)
	{	
		if(Locate(L,str[i])==0)
		{
				//未重复的部分,也就是第一次录入的部分 
				p=new LNode;
				p->data.Time=1;	//结点初始化频次为1 
				p->data.key=str[i];
				p->next=NULL;
				r->next=p;
				r=p; 
		} 
		
	}
	return;
}

void Rate2(LinkList L)
{
	fstream out;
	out.open("OutFile1.txt",fstream::out | ios_base::trunc);
	LinkList p=L->next;
	//利用顺序表计算单词概率 
	int i=1; 
	cout<<"单词"<<" "<<"频次"<<" "<<"概率"<<endl; 
	while(i<=t)
 	{ 	
		if(flag[i]==1)//如果是第一个元素,就输出 
		{
				fre[i]=p->data.Time;
				rate[i]=(double)fre[i]/t;
				rate[i]=rate[i]*10000;
				if(rate[i]>setrate)
				{
					out<<p->data.key<<" "<<fre[i]<<endl;
					cout<<"i:"<<i<<" "<<p->data.key<<" "<<fre[i]<<"/"<<t<<" "<<rate[i]<<endl;
				}	
				if(p->next) p=p->next;	
				else break;
		}
		i++;
	}	
 } 

int InitList(LinkList &L)
{
	LinkList p=L;           
    while(p->next){
           p=p->next;
		   p->data.Time=1;	   
    }           
	return 0;
}

int ListLength(LinkList L)
{
    LinkList p;
    p=L->next;  //p指向第一个结点
    int i=0;             
    while(p){//遍历单链表,统计结点数
           i++;
           p=p->next;   
    } 
    return i;                             
 }

void Sort2(LinkList &L)
{
    int n=ListLength(L);
	int i; //用i记录已经比好,置于末尾的元素 
	int j;//用j记录已比较的轮次 
    LinkList p;
    for(i=0;i<n-1;i++)//n个元素要比较n-1轮 
    {
        p=L->next;
        j=0;//比较完一轮后,j归零 
        while(p&&j<n-1-i)//和2轮for循环的本质一样 
        {
            if(p->data.key > p->next->data.key)
            {
                Word t=p->data;
                p->data=p->next->data;
                p->next->data=t;
            }
            p=p->next;
            j++;
        }
    }
    return;
}

void Output2(LinkList &L)
{
	int i;
	fstream out;
	out.open("OutFile2.txt",fstream::out | ios_base::trunc);
	out<<"单词 词频"<<endl; 
	Sort2(L);
	LinkList p=L->next;
	while(p)
	{ 	
		out<<p->data.key<<" "<<p->data.Time<<endl;
		p=p->next;
	} 
	return;
}

void Search2(LinkList L)
{
	Output2(L);
	cout<<"基于链式存储结构,请输入你要查找的单词:" <<endl;
	string ch;
	cin>>ch;
	
	LARGE_INTEGER fr;
    LARGE_INTEGER st;
    LARGE_INTEGER ed;
    double time;
	QueryPerformanceFrequency(&fr);
    QueryPerformanceCounter(&st);
	 
	LinkList p;
    p=L;
    int i=0;             
    while(p->next)
	{ 
           p=p->next; 
		   if(p->data.key==ch)
		   {
		   		QueryPerformanceCounter(&ed);
				time = (double)(ed.QuadPart - st.QuadPart) / (double)fr.QuadPart;
		   		cout<<"此单词的词频为:"<<p->data.Time<<endl;
				cout<<"查找所花费的次数:"<<Locate(L,ch)<<endl;	
				cout<<"查找所花费的时间:"<<setprecision(6)<<time*1000000<<"ns"<<endl;
				cout<<"平均查找长度:"<<(factnum+1)/2<<endl;
				return;
		   }
	}
	cout<<"查找失败!"<<endl; 
 } 
 
void Search2_1(LinkList L,string ch)
{
	Output2(L);
	cout<<"基于链式存储结构:" <<endl;
	LinkList p;
    p=L;
    int i=0;
    
	LARGE_INTEGER fr;
    LARGE_INTEGER st;
    LARGE_INTEGER ed;
    double time;
	QueryPerformanceFrequency(&fr);
    QueryPerformanceCounter(&st);              
    while(p->next)
	{ 
           p=p->next; 
		   if(p->data.key==ch)
		   {
		   		QueryPerformanceCounter(&ed);
				time = (double)(ed.QuadPart - st.QuadPart) / (double)fr.QuadPart;
		   		cout<<"此单词的词频为:"<<p->data.Time<<endl;
				cout<<"查找所花费的次数:"<<Locate(L,ch)<<endl;
				cout<<"查找所花费的时间:"<<setprecision(6)<<time*1000000<<"ns"<<endl;
				cout<<"平均查找长度:"<<(factnum+1)/2<<endl;
				return;
		   }
	}
	cout<<"查找失败!"<<endl; 
 } 

void PrintText()
{
	int i=1;
 	while(i<=t)
 	{ 	
 		cout<<str[i]<<" ";
		i++;
	}
	cout<<endl;
}

void PrintWord()
{
	int i=1;
	while(i<=t)
	{
		
		cout<<"单词序号:"<<i<<"   /"<<str[i]<<"/"<<" ";
		cout<<str[i].length()<<endl;
		i++;
	}
	if(i==t)cout<<"单词数组已全部打印"<<endl; 
 	cout<<"除去数字外,本文一共"<<i-1<<"个英文单词"<<endl;	
}

void Output1(SSTable &L)
{
	int i;
	fstream out;
	out.open("OutFile1.txt",fstream::out | ios_base::trunc);
	out<<"单词 词频"<<endl;
	//out<<"单词 词频 概率(万分之一)"<<endl; 
	for(i=1;i<=L.length;i++)
	{
		//cout<<L.R[i].key<<" "<<L.R[i].Time<<"/"<<t<<" "<<setprecision(10)<<10000*L.R[i].Time/t<<endl;
		//out<<L.R[i].key<<" "<<L.R[i].Time<<"/"<<t<<" "<<setprecision(10)<<10000*L.R[i].Time/t<<endl;
		out<<L.R[i].key<<" "<<L.R[i].Time<<endl;
		//cout<<L.R[i].key<<" "<<L.R[i].Time<<endl;
	}
	return ;
}

void Search1_Order(SSTable &L,string a)
{

	int flag=0;int i;
	LARGE_INTEGER fr;
    LARGE_INTEGER st;
    LARGE_INTEGER ed;
    double time;
	QueryPerformanceFrequency(&fr);
    QueryPerformanceCounter(&st); 
	for(i=1;i<i<=L.length;i++)//1000000
	{
		
		if(a==L.R[i].key) 
		{	
			flag=1;
			break;
		}
	}
	QueryPerformanceCounter(&ed);
	time = (double)(ed.QuadPart - st.QuadPart) / (double)fr.QuadPart;
	
	if(flag==1)
	{
			cout<<"基于顺序表的查找:"<<endl; 
			cout<<"此单词的词频为:"<<L.R[i].Time<<endl;
			cout<<"查找所花费的次数:"<<i<<endl;
			cout<<"查找所花费的时间:"<<setprecision(6)<<time*1000000<<"ns"<<endl;
			cout<<"平均查找长度:"<<(L.length+1)/2<<endl;
	}
	else cout<<"查找失败"<<endl; 
	return;
}

void Search1_Half(SSTable &ST,string key)
{

	cout<<"基于折半查找:"<<endl; 
	int low=1;
	int high=ST.length;
	int k=0;
	int flag=0;
	int mid=0;
	
	LARGE_INTEGER fr;
    LARGE_INTEGER st;
    LARGE_INTEGER ed;
    double time;
	QueryPerformanceFrequency(&fr);
    QueryPerformanceCounter(&st); 
	while(low<=high)
	{	
		k++;
    	mid=(low+high)/2;
	    if(key==ST.R[mid].key) 
		{ 
			flag=1;
			break;
		}
	    else if(key<ST.R[mid].key) high=mid-1;//前一子表查找
	    else low=mid+1;                       //后一子表查找
   	}
   	QueryPerformanceCounter(&ed);
	time = (double)(ed.QuadPart - st.QuadPart) / (double)fr.QuadPart;
	if(flag==1)
	{
			cout<<"此单词的词频为:"<<ST.R[mid].Time<<endl;	
			cout<<"查找所花费的次数:"<<k<<endl;
			cout<<"查找所花费的时间:"<<setprecision(6)<<time*1000000<<"ns"<<endl;
			cout<<"平均查找长度:"<<log(ST.length)/log(2)<<endl;
	}
	else 
	{
		cout<<"查找失败"<<flag<<endl;
	} 
	return;
}

void Preprocessing()
{
	//读入 
	fstream in;
 	in.open("InFile.txt",ios::in);
 	if(!in)
 	{
  		cout<<"Can not open the file!"<<endl;
 		return;
    }
	//存入内存 
	int i=1;//从第一个单词开始,str[0]空置不用 
 	while(!in.eof())
 	{ 	
 		in>>str[i];
		int j=str[i].length();//单词字母数 
		//首字母大写转为小写 
		for(int n=0;n<j;n++)
		{
			if(str[i][n]>='A'&&str[i][n]<='Z')
					str[i][n]=str[i][n]+32;
		}
		
		//非字母字符标记统计
		int num=0; 
		for(int n=0;n<j;n++)
		{
			if(str[i][n]<'a'||str[i][n]>'z')
			{
				//cout<<"非字母:"<<str[i][n]<<endl;
				str[i][n]=35;
			//	cout<<"转化为:"<<str[i][n]<<endl;
				num++;
			}			
		}
		//非字母字符移动至字符串最后 
		for(int n=0;n<j;n++)
		{
			for(int k=0;k<j;k++)
				if(str[i][k]==35)
				{
					str[0][0]=str[i][k];
					str[i][k]=str[i][k+1];
					str[i][k+1]=str[0][0];
				}		
		}
		str[i]=str[i].substr(0,j-num);
		if(str[i][0]=='\0')
		{
			//cout<<"空格位置:"<<i<<endl;//system("pause");
			i--;//如果当前接收的是一个空串,则接受下一个字符串 
		}
		//统计单词数 
		i++;
	}
	t=i-1;//t为常量 
//	cout<<"单词总数:"<<t<<endl;
	
	//频率数组初始化 
	for(i=1;i<t;i++)
	{ 
		fre[i]=1;
	}
		
	return;
}



void PrintMenu()
{
	cout<<"基于不同策略的英文单词的词频统计和检索系统"<<endl;
	//cout<<"文本位置 ";system("cd"); 
	cout<<"单词总数:"<<t<<endl;
	if(t>800)cout<<"注意:当前单词数量过多,可能导致程序崩溃"<<endl;
	cout<<endl;
	cout<<"---菜单---"<<endl; 
	cout<<"1.基于顺序表的查找"<<endl; 
	cout<<"2.基于链表的查找"<<endl;
	cout<<"3.基于二叉排序树的查找"<<endl;
	cout<<"4.哈希表"<<endl; 
	cout<<"5.单词总数"<<endl;
	cout<<"6.单词数组打印"<<endl;
	cout<<"7.高频词输出"<<endl; 
	cout<<"8.文章打印"<<endl;
	cout<<"9.比较"<<endl; 
	cout<<"0.链表:删除低频词"<<endl;
	cout<<"a.二叉排序树:删除低频词"<<endl; 
	cout<<"b.以二叉排序树结构输出"<<endl; 
	cout<<"----------"<<endl;
	cout<<"请按相应的数字键进行选择:"<<endl; 
}

void PrintMenu1()
{
	cout<<"基于不同策略的英文单词的词频统计和检索系统"<<endl; 
	cout<<"单词总数:"<<t<<endl;
	if(t>800)cout<<"注意:当前单词数量过多,可能导致程序崩溃"<<endl; 
	cout<<endl;
	cout<<"---菜单---"<<endl; 
	cout<<"1.顺序查找"<<endl; 
	cout<<"2.折半查找"<<endl;
	cout<<"3.删除低频词"<<endl;
	cout<<"4.返回上一级"<<endl; 
	cout<<"----------"<<endl; 
	
	cout<<"请按相应的数字键进行选择:"<<endl; 
}

void PrintMenu4()
{
	cout<<"基于不同策略的英文单词的词频统计和检索系统"<<endl; 
	cout<<"单词总数:"<<t<<endl;
	if(t>800)cout<<"注意:当前单词数量过多,可能导致程序崩溃"<<endl;
	cout<<endl;
	cout<<"---菜单---"<<endl; 
	cout<<"1.开放地址法"<<endl; 
	cout<<"2.链地址法"<<endl;
	cout<<"3.返回上一级"<<endl; 
	cout<<"----------"<<endl;
	cout<<"请按相应的数字键进行选择:"<<endl; 
}

void QuickSort1(SSTable &L, int l, int r)//l=1,r=L.length
{
    if (l < r)
    {
        int i=l, j=r;Word x = L.R[l];
        while (i < j)
        {
            while(i < j && L.R[j].key>= x.key) // 从右向左找第一个小于x的数
                j--;  
            if(i < j) 
                L.R[i++] = L.R[j];
            
            while(i < j && L.R[i].key < x.key) // 从左向右找第一个大于等于x的数
                i++;  
            if(i < j) 
                L.R[j--] = L.R[i];
        }
        L.R[i] = x;
        QuickSort1(L, l, i - 1); // 递归调用 
        QuickSort1(L, i + 1, r);
    }
}

void Sort1(SSTable &L)
{
	int i=0,j=0;//外i内j 
	for(i=1;i<=L.length;i++)//外层循环n-1 
		for(j=1;j<=L.length-i;j++)//内层循环n-1-i 
		{
			if(L.R[j].key>L.R[j+1].key)
			{
				Word temp;
				temp=L.R[j];
				L.R[j]=L.R[j+1];
				L.R[j+1]=temp;
			}
		}
}

void Del1(SSTable &L,int no)
{	
	no=no+1;
	int i;
	if(no>L.length||no<1)
	{
		cout<<"Sorry,the position to be deleted is invalid!"<<endl;
		return;
	}
	for (i=no;i<L.length;i++) 
	{
		L.R[i-1]=L.R[i];
	}
	L.length--;
	return; 
}

void DelLowRate1(SSTable &L)
{
	cout<<"删除前表长:"<<L.length<<endl; 
	for(int i=1;i<L.length;i++)
	{
		if(10000*(double)L.R[i].Time/t<100)
		{
			Del1(L,i);
			i--;//key	
		} 		
	}
	cout<<"删除后表长:"<<L.length<<endl; 
	for(int i=1;i<L.length;i++)
	{
		if(10000*(double)L.R[i].Time/t)
		{
			cout<<"剩余"<<L.R[i].key<<" "<<L.R[i].Time<<" "<<10000*(double)L.R[i].Time/t<<endl; 
		}		
	}
}

void Del2(LinkList &L,int no)
{
	int i;int j;
	if(no>ListLength(L)||no<1)
	{
		cout<<"Sorry,the position to be deleted is invalid!"<<endl;
		return;
	}
	LinkList p,q;
	p=L;j=0;
	while(p && (j<no-1))
	{
		p=p->next;
		j++; 
	}
	q=p->next;
	p->next=q->next;
	delete q;
	return; 
}

void DelLowRate2(LinkList &L)
{
	cout<<"删除前表长:"<<ListLength(L)<<endl; 
	LinkList p;
    p=L;
    int i=0;             
    while(p->next)
	{ 
			i++;
           p=p->next;
		   if(10000*(double)p->data.Time/t<100)
		   {		
				Del2(L,i);
				i--;//key	
			}			
	}
	cout<<"删除后表长:"<<ListLength(L)<<endl; 
	p=L;            
    while(p->next)
	{ 
        p=p->next; 
		if(10000*(double)p->data.Time/t)
		{
			cout<<"剩余"<<p->data.key<<" "<<p->data.Time<<" "<<10000*(double)p->data.Time/t<<endl; 
		}		
	}
}

//根据节点的三种情况来删除节点
int Delete(BSTree	& p)
{
	BSTree q, s;
	if (p->rchild==NULL)
	{
		q = p; p = p->lchild; free(q);cout<<"删除完成1"<<endl;
	}
	else if ((p)->lchild==NULL)
	{
		q = p; p = p->rchild; free(q);cout<<"删除完成2"<<endl;
	}
	else
	{
		q = p; s = p->lchild;
		while (s->rchild)
		{
			q = s; s = s->rchild;
		}
		p->data = s->data;
		if (q != p)
			q->rchild = s->lchild;
		else
			q->lchild = s->lchild;
		free(s);
		cout<<"删除完成3"<<endl;
	}
	return true;
}

int DeleteBST(BSTree& T, string key)
{
	if (!T)
		return false;
	else
	{
		if (key == T->data.key)
		{
			return Delete(T);
		}
		else if (key<T->data.key)
		{
			return DeleteBST(T->lchild,key);
		}
		else
		{
			return DeleteBST(T->rchild,key);
		}
	}
}

void DeleteBST2(BSTree &T,string key) {
  //从二叉排序树T中删除关键字等于key的结点
  BSTree p=T;BSTree f=NULL;                     			//初始化
  BSTree q;
  BSTree s;
  while(p)
  {                  
	   if (p->data.key == key) 
	   {
	   	 	break;   
	   } 
	   f=p; 
	   if (p->data.key> key)
	   {
	   		p=p->lchild;     	
	   }
	   else 
	   {
	   		p=p->rchild;  	                 
	   }
  }
	if(!p) 
	{
		return; 	
	}     
	if ((p->lchild)&& (p->rchild))
	{ 	
	     q = p;
		 s = p->lchild;
	     while (s->rchild)                		
	       {q = s; s = s->rchild;}	         		
	     p->data = s->data;               		
	     if(q!=p)
		 {
			 q->rchild = s->lchild;     
		 }
		else q->lchild = s->lchild;    		
     	delete s;
  	}//if
	else
	{
		if(!p->rchild) 
		{     
			   		
			  q = p; 	     
			  p = p->lchild; 
		
		}//else if
		else if(!p->lchild)
				{   
				 	q = p;	  p = p->rchild;
			 	}//else if
	  if(!f) 
	  {
	  	T=p;  
	  }               			
	  else
	  {
	  	if (q==f->lchild) 
		  {
		  	f->lchild = p;   	 
		  }
		  else 
		  {
		  	
		  		f->rchild = p;     
		  }
	  }
	  delete q;
	}
}//DeleteBST

int DelLowRate3(BSTree &T)
{
	if(T)
	{
 		DelLowRate3(T->lchild);
		if(10000*T->data.Time/t>100)
		{
			string key1=T->data.key;
			//DeleteBST(T,key1);
			Insert(T1,T->data);//Word e
			factnum++;
		}
		else
		{
		//	cout<<"保留:"<<T->data.key<<" "<<10000*(double)T->data.Time/t<<endl;
		}		
		DelLowRate3(T->rchild);
	}
	return 0; 
}

void MenuLinearList(SSTable &L)
{
	//菜单 
	system("cls");
	PrintMenu1();
	char ch;
	cin>>ch;
	//Sort1(L);
	QuickSort1(L,1,L.length);
	Output1(L);//输出 
	string sk;
	switch(ch)
 	{
 		case '1':	cout<<"顺序查找,输入你要查找的单词:"<<endl;
					cin>>sk;
					Search1_Order(L,sk);system("pause");system("cls");break;
					
		case '2':	cout<<"折半查找,输入你要查找的单词:"<<endl;
					cin>>sk;	
					Search1_Half(L,sk);system("pause");system("cls");
					break;
					
		case '3':   DelLowRate1(L);system("pause");system("cls");break;
		
		case '4':	return;
	}
}

int Hash(string key)
{
	int n;
	n=key[0]-'a';//用ASCII码作差,计算首字母的序号 
	int result=100*n;//每一个字母分配100个数组空间,a序号就是0,空间为0-100. 
	return result;
} 

int Search4(HashTable HT[],string key){
  //在哈希表中查找,若查找成功,返回序号,否则返回-1 
  int H0=Hash(key);     	                   			//计算哈希地址
  int Hi;
  if (HT[H0].key==NULLKEY) return -1;		//若单元H0为空,则所查元素不存在
  else if (HT[H0].key==key) return H0;		//若单元H0中元素的关键字为key,则查找成功
  		else{             
     			for(int i=1;i<m;++i)
				{                
					 Hi=(H0+i)%m;     		//按照线性探测法计算下一个哈希地址Hi
			        	if (HT[Hi].key==NULLKEY) return -1;	//若单元Hi为空,则所查元素不存在
		       		 	else if (HT[Hi].key==key) return Hi; 	//若单元Hi中元素的关键字为key,则查找成功
    			 }//for
     			return -1;
  			}//else
}

int CheckHash(HashTable HT[],string key){
  int H0=Hash(key);     	                   		
  int Hi;
  if (HT[H0].key==NULLKEY) return -1;	
  else if (HT[H0].key==key) return H0;		
  else{             
     for(int i=1;i<m;++i){               
		 Hi=(H0+i)%m;     	
        if (HT[Hi].key==NULLKEY) return -1;
        else if (HT[Hi].key==key)
        	{
        		HT[Hi].Time++;
        		return Hi;
			}	 	
     }//for
     return -1;
  }//else
}

void QuickSort(HashTable HT[], int l, int r)
{
    if (l < r)
    {
        int i = l, j = r;HashTable x = HT[l];
        while (i < j)
        {
            while(i < j && HT[j].key>= x.key) // 从右向左找第一个小于x的数
                j--;  
            if(i < j) 
                HT[i++] = HT[j];
            
            while(i < j && HT[i].key < x.key) // 从左向右找第一个大于等于x的数
                i++;  
            if(i < j) 
                HT[j--] = HT[i];
        }
        HT[i] = x;
        QuickSort(HT, l, i - 1); // 递归调用 
        QuickSort(HT, i + 1, r);
    }
}

void Sort4(HashTable HT[])
{
	int i=0;int j;int res=0;
	for(i=0;i<=26;i++)
	{
			for(j=i*100+1;j<(i+1)*100;j++)
			{
				if (HT[j].key=="\0")
				{
					res=j;
					break;
				}
			}
			QuickSort(HT,i*100+1,res);//不能为(i+1)*100,因为后面都是空,只能到res				
	}	
	return;	 
} 

void Output4(HashTable HT[])
{
	int i;
	fstream out;
	out.open("OutFile4.txt",fstream::out | ios_base::trunc);
	out<<"单词 词频"<<endl;
	for(int i=1;i<m;i++)
		if(HT[i].key!="\0")
		{
			out<<HT[i].key<<" "<<HT[i].Time<<endl; 
		}	
	return;
}


void Input4(HashTable HT[])
{
	//哈希表初始化 
	for(int i=1;i<MAXSIZE;i++)//不能有等于,越界就崩溃 
	{
			HT[i].key="\0";
			//cout<<i<<endl;
	}
	//构建 
	for(int i=1;i<=5000;i++)//300-5000
	{
		if(str[i][0]=='\0')break; //或者i==t 
		while(1)
		{
			int res=CheckHash(HT,str[i]);//在此实现 
			if(res==-1)break;
			else 
			{
				i++;
				//Time++;无法在此实现 
			}
		}
		int n;
		n=str[i][0]-'a';
		int max=100*n+100;
		int p=max-100+1;//从1开始,0开始会有错误 
		while(p<max)
		{
			if(HT[p].key=="\0")//用checkhash记录频次 &&CheckHash(HT,str[i])==-1
			{
				HT[p].key=str[i];
				HT[p].Time=1;
				//cout<<HT[p].key<<"已录入第"<<p<<"个位置"<<endl;
				break;
			}
			else	p++;
		}
	}
}

void HashOpenAddress(HashTable HT[])
{	
	Input4(HT);
	Sort4(HT);
	Output4(HT);
	string sk;
	cout<<"开放地址法,输入你要查找的单词:"<<endl;
	cin>>sk;
	
	LARGE_INTEGER fr;
    LARGE_INTEGER st;
    LARGE_INTEGER ed;
    double time;
	QueryPerformanceFrequency(&fr);
    QueryPerformanceCounter(&st); 
	int result=Search4(HT,sk);
	QueryPerformanceCounter(&ed);
	time = (double)(ed.QuadPart - st.QuadPart) / (double)fr.QuadPart;
	
	if(result!=-1)
	{
		cout<<"此单词的词频为:"<<HT[result].Time<<endl;
		cout<<"查找所花费的次数:"<<result%100<<endl;
		cout<<"查找所花费的时间:"<<setprecision(6)<<time*1000000<<"ns"<<endl;
		cout<<"平均查找长度:"<<factnum/26<<endl;
		cout<<"在第"<<result<<"位置找到"<<endl;
	}
	else
	{
		cout<<"查找失败"<<endl;
	}
	return;
}

void HashOpenAddress_1(HashTable HT[],string sk)
{	

	Input4(HT);
	Sort4(HT);
	Output4(HT);
	
	cout<<"开放地址法:"<<endl;
	
	LARGE_INTEGER fr;
    LARGE_INTEGER st;
    LARGE_INTEGER ed;
    double time;
	QueryPerformanceFrequency(&fr);
    QueryPerformanceCounter(&st); 
	int result=Search4(HT,sk);
	QueryPerformanceCounter(&ed);
	time = (double)(ed.QuadPart - st.QuadPart) / (double)fr.QuadPart;
	
	if(result!=-1)
	{
		cout<<"此单词的词频为:"<<HT[result].Time<<endl;
		cout<<"查找所花费的次数:"<<result%100<<endl;
		cout<<"查找所花费的时间:"<<setprecision(6)<<time*1000000<<"ns"<<endl;
		cout<<"平均查找长度:"<<factnum/26<<endl;
		cout<<"在第"<<result<<"位置找到"<<endl;
	}
	else
	{
		cout<<"查找失败"<<endl;
	}
	return;
}

void Output5(ALGraph G)
{
	fstream out;
	out.open("OutFile5.txt",fstream::out | ios_base::trunc);
	int i;
	//HCSort(G);
	out<<"单词"<<" "<<"词频"<<endl;
	for(i=1;i<=26;i++)
	{
		WordNode *h;
		h=G.Letter[i].firstarc;
		while(h!=NULL)
		{
			out<<h->data.key<<" "<<h->data.Time<<endl;
			h=h->nextarc;
		}
	} 
	out.close();
	return;
}

void Sort5(ALGraph &G)
{
    int i;
    for(i=1;i<=26;i++)
    {
    	WordNode *p,*q;
	    Word t;
	    for (p=G.Letter[i].firstarc; p!=NULL; p=p->nextarc) 
		{
	        for(q=p->nextarc; q!=NULL; q=q->nextarc) 
			{
	            if(p->data.key>q->data.key) 
				{
	                t=q->data;
	                q->data=p->data;
	                p->data=t;
	            }
	        }
	    }
	}
    return;
}

void HashChainAddress(ALGraph G,string sk)
{
	cout<<"基于链地址法:"<<endl;
	Sort5(G);
	Output5(G);
	int k;
	int i;
	k=sk[0];
	k=k-96;
	WordNode *h;
	int flag=0;
	h=G.Letter[k].firstarc;
	int j=0;
	int r;
	
	LARGE_INTEGER fr;
    LARGE_INTEGER st;
    LARGE_INTEGER ed;
    double time;
	QueryPerformanceFrequency(&fr);
    QueryPerformanceCounter(&st); 
	
	while(h!=NULL)
	{
		j++;
		if(h->data.key==sk)
		{
			flag=j;
			r=h->data.Time;
		}
		h=h->nextarc;
	}
	
	QueryPerformanceCounter(&ed);
	time = (double)(ed.QuadPart - st.QuadPart) / (double)fr.QuadPart;
	
	if(flag>0)
	{
		
		cout<<"此单词的词频为:"<<r<<endl;
		cout<<"查找该单词所花的次数:"<<flag+1<<endl;flag=(t+1)/2;
		cout<<"查找所花费的时间:"<<setprecision(6)<<time*1000000<<"ns"<<endl;
		cout<<"平均查找长度:"<<flag<<endl;
	}
	else
	{
		cout<<"查找失败"<<endl;
	}
	return;
}

void Input5(ALGraph &G)
{
	int i;
	//初始化顶点表 
	for(i=1;i<=26;i++)
	{
		G.Letter[i].letter_no=i;
		G.Letter[i].firstarc=NULL;
	}
	int num=0;//单词计数 
	for(i=0;i<t;i++)
	{
		int k;
		k=Hash_int(i);//判断单词首字母序号 
		WordNode *h,*w,*e,*p,*r;
		h=G.Letter[k].firstarc;//h是单词的首字母表头结点 
		if(h==NULL)//表头结点为空,录入 
		{
			p=new WordNode;
			p->data.key=str[i];
			p->data.Time=fre[i];
			p->nextarc=NULL;
			G.Letter[k].firstarc=p;
			num++;
		}
		else
		{
			int flag=0;
			while(h!=NULL)
			{
				r=h;
				if(h->data.key==str[i])//遍历一个首字母的链表,如果相同,频次自加 
				{
					h->data.Time++;
					flag=1;
				}
				h=h->nextarc;// 
			}
			if(flag==0)//如果没有相同的,就录入 
			{
				p=new WordNode;
				p->data.key=str[i];
				p->data.Time=fre[i];
				p->nextarc=NULL;
				r->nextarc=p;
				num++;
			}
		}
	}
	factnum=num;//有效单词数 
	return;
}

void MenuHash(HashTable HT[],ALGraph G)
{
	//菜单 
	system("cls");
	PrintMenu4();
	char ch;
	cin>>ch;
	string sk;
	switch(ch)
 	{
 		case '1':	HashOpenAddress(HT);system("pause");system("cls");break;
					
		case '2':	cout<<"链地址法,输入你要查找的单词:"<<endl;
					cin>>sk;
					HashChainAddress(G,sk);system("pause");system("cls");
					break;
					
		case '3':   return;
	}
}

void PrintMenu7()
{
	cout<<"基于不同策略的英文单词的词频统计和检索系统"<<endl; 
	cout<<"单词总数:"<<t<<endl;
	if(t>800)cout<<"注意:当前单词数量过多,可能导致程序崩溃"<<endl; 
	cout<<endl;
	cout<<"---菜单---"<<endl; 
	cout<<"1.顺序表"<<endl; 
	cout<<"2.链表"<<endl;
	cout<<"3.二叉排序树"<<endl;
	cout<<"4.开放地址法"<<endl;
	cout<<"5.链地址法"<<endl;
	cout<<"6.概率区间筛选"<<endl;
	cout<<"7.返回上一级"<<endl; 
	cout<<"----------"<<endl;
	cout<<"请按相应的数字键进行选择:"<<endl; 
}

int TraverseOut1(BSTree T,int &i)
{
	if(T)
	{
		fstream out;
		out.open("OutFile1.txt",fstream::out | ios_base::trunc);
		TraverseOut1(T->lchild,i);
		i++; 
		if(10000*(double)T->data.Time/t<setrate)//
		{
			out<<T->data.key<<" "<<T->data.Time<<endl;	
			cout<<"i:"<<i<<" "<<T->data.key<<" "<<T->data.Time<<"/"<<t<<" "<<10000*(double)T->data.Time/t<<endl;	
		}
		TraverseOut1(T->rchild,i); 
	}
	return 0; 
}

void Rate3(BSTree T)
{
	int i=0;
	cout<<"单词"<<" "<<"频次"<<" "<<"概率(万分之一)"<<endl;
	TraverseOut1(T,i); 	
}

void Rate4(HashTable HT[])
{
	fstream out;
	out.open("OutFile1.txt",fstream::out | ios_base::trunc);
	cout<<"单词"<<" "<<"频次"<<" "<<"概率(万分之一)"<<endl;
	for(int i=1;i<m;i++)
	if(HT[i].key!="\0")
	{
		if(10000*(double)HT[i].Time/t>setrate)
		{
			out<<HT[i].key<<" "<<HT[i].Time<<endl;
			cout<<"i:"<<i<<" "<<HT[i].key<<" "<<HT[i].Time<<"/"<<t<<" "<<10000*(double)HT[i].Time/t<<endl;		 
		}
		
	}
}

void Rate5(ALGraph G)
{
	fstream out;
	out.open("OutFile1.txt",fstream::out | ios_base::trunc);
	int k=1; 
	//sort
	Sort5(G);
	cout<<"单词"<<" "<<"频次"<<" "<<"概率(万分之一)"<<endl;
	for(int i=1;i<=26;i++)
	{
		WordNode *h;
		h=G.Letter[i].firstarc;
		while(h!=NULL)
		{
			if(10000*(double)h->data.Time/t>setrate)
			{
				out<<h->data.key<<" "<<h->data.Time<<endl;		 
				cout<<"i:"<<k<<" "<<h->data.key<<" "<<h->data.Time<<"/"<<t<<" "<<10000*(double)h->data.Time/t<<endl;		 	
			}		 
			h=h->nextarc;
			k++;
		}
	} 
	return;
}


void Rate6(SSTable L)
{

	cout<<"请输入筛选的概率区间:"<<endl; 
	cin>>setrate>>setrate2; 
	int i=0;//fre[5]=1;
	cout<<"单词"<<" "<<"频次"<<" "<<"概率(万分之一)"<<endl;
	while(i<=factnum)
 	{ 	
		if(flag[i]==1)//如果是第一个元素,就输出 
		{
				fre[i]=L.R[i].Time; 
				rate[i]=(double)fre[i]/t;
				rate[i]=rate[i]*10000;
				if(rate[i]>setrate&&rate[i]<setrate2)//&&rate[i]<100000
				{
					cout<<"i:"<<i<<" "<<L.R[i].key<<" "<<fre[i]<<"/"<<t<<" "<<rate[i]<<endl;
				}		
		}
		i++;
	}	
}


void MenuHighRate(SSTable L,LinkList L1,BSTree T,HashTable HT[],ALGraph G)
{
	//频率初始化 
	for(int i=1;i<t;i++)
	{
		fre[i]=1;
		rate[i]=0;
	}
		
	//菜单 
	system("cls");
	PrintMenu7();
	char ch;
	cin>>ch;
	string sk;
	switch(ch)
 	{
 		case '1':	Rate1(L);system("pause");system("cls");break;
					
		case '2':	Rate2(L1);system("pause");system("cls");break;
					
		case '3':   Rate3(T);system("pause");system("cls");break;
		
		case '4':	Input4(HT);system("pause");system("cls");Sort4(HT);Rate4(HT);break;
		
		case '5':	Rate5(G);system("pause");system("cls");break;
		
		case '6':	Rate6(L);system("pause");system("cls");break;
					
	}
}

void Compare(SSTable L,LinkList L1,BSTree T,HashTable HT[],ALGraph G)
{
	cout<<"请输入您要查找的单词:"<<endl;
	string sk;
	cin>>sk;
	
	Sort1(L);
	Sort2(L1);
	Sort4(HT);
	Sort5(G);
	
	Search1_Order(L,sk);cout<<endl;
	Search1_Half(L,sk);cout<<endl;
	Search2_1(L1,sk);cout<<endl;
	Search3_1(T,sk);cout<<endl;
	HashOpenAddress_1(HT,sk);cout<<endl;
	HashChainAddress(G,sk);cout<<endl;
	//若查找的是文章中最后一个词,会出现查找失败的情况。 
	
	Output1(L);
	Output2(L1);
	Output3(T);
	Output4(HT);
	Output5(G);
	cout<<"统计数据已存入文件夹!"<<endl;
	
	return; 
}



int main()
{
	//system("color 0B");
	Preprocessing();//得到单词总数t与单词数组str[t] 
	
	//顺序存储结构 
	SSTable L;InitList(L);
	Input1(L);//录入单词,词频统计 


	//链式存储结构
	LinkList L1;
	Input2(L1);//录入单词

	
	//二叉排序树
	BSTree T;
	InputTree(T);
	//InputTree(T);

	//开放地址法 
	HashTable HT[m];
	
	//链地址法
	ALGraph G;
	Input5(G); 

	//菜单 
	while(1)
	{	
	
		system("cls");
		PrintMenu();
		//system("ver");
		char ch;
		cin>>ch;
		switch(ch)
	 	{
	 		case '1':MenuLinearList(L);break;
			case '2':Search2(L1);system("pause");system("cls");break;break;
			case '3':Search3(T);system("pause");system("cls");break;break;
			case '4':MenuHash(HT,G);break;
			case '5':cout<<"单词总数:"<<t<<endl;system("pause");system("cls");break;
			case '6':PrintWord();system("pause");system("cls");break;
			case '7':MenuHighRate(L,L1,T,HT,G);break;
			case '8':PrintText();system("pause");system("cls");break; 
			case '9':Compare(L,L1,T,HT,G);system("pause");system("cls");break;
			case '0':DelLowRate2(L1);system("pause");system("cls");break;
			case 'a':factnum=1;DelLowRate3(T);T=T1;system("pause");system("cls");break;
			case 'b':TraverseOut2(T);system("pause");system("cls");break;
		}
		//system("pause");system("cls");
	}
	return 0;
}
课程设计的总结
1. 问题与解决方法

	递归函数中读文件,每递归一次就要建立一个流对象,能行吗?
没关系。
用ios::app,文件指针保留在上次读的位置,可以接着读。

	为什么二叉树要初始化?不初始化会怎么样?
什么是初始化?初始化,就是第一次赋值。
任何一个变量在使用之前必须要先对它进行初
始化。不初始化的话使用它就没有实际意义了。
在实际编程中,习惯上在定义变量的时候就对它
进行初始化,这是一个很好的编程习惯。

	怎么对输入的字符进行处理?
预处理环节
先读文件,把单词读进一个(字符串)单词数组
同时对当前字符串进行处理,先把所有大写字母变为小写字母
再把标点符号,数字等不是小写字母的字符替换为#号,冒泡排序,依次把#号往后挪动,挪动至字符串末尾,这个过程中,记录#号个数,再通过字符串函数substr将其去除。

2. 尚存在的问题
窗体还没写。每个句子之间必须隔开,有一个空格。

3.总结
	函数的命名一定要规范,有的同学用拼音来命名,代码很难读。
 

	写代码的时候,快速定位错误位置和原因很重要。
不会调试,用cout这个笨办法可以逐步查错。
	注意数组的开始位置,是1还是0。
注意比较大小的时候,到底有没有等号。
注意函数的传给参数到底是从1开始,还是从0开始,是位置序号,还是数组下标。
	程序的逻辑至关重要
每种返回值代表的含义不要混淆用反,以保证后续的操作正确。
	写程序之前,建议先写出这个函数的伪代码。
	查重和搜索不能用同一个函数,查重函数肩负着统计频次的功能,而搜索函数不用。
	二叉排序树的查重函数,运用递归思想,遍历树的同时,将不断的改变树的根节点,所以在使用这个函数的时候,要新建一个临时的树,初始化,保存当前树的数据,用这个临时的树进行搜索。
	BSTree temp;
	InitTree(temp);
	temp=T;
	int res=InputCheck3(temp,str[i]);

	在数组的数据结构中,要想对数据的进行修改,就必须知道数据的位置。
比如,一个非数组结构的单词结点,比如链表,二叉树,再查重函数结束之后,将无法得知结点的位置,就算知道结点的位置,也不方便对该结点进行修改,因为要再次遍历。所以,在查重的时候,就应该顺便对结点的频次数据进行加1的修改。
		
	录入多个单词并统计频率
Int 查重函数()
{
	如果重复,频率加1,返回一个标记值。
	如果不重复,返回另一个标记值
}
While(未录入完毕)
{
If(查重函数说没有重复)
{
录入新单词;
设置单词频率为1;
}
}
	在对链表进行遍历操作的时候,建立一个临时的指针,让它等于待操作链表,防止对原链表进行修改。
LinkList p=L;           
while(p->next)//严防指针越界,不可以对空指针进行操作。
{
 		p=p->next;	 
		对p进行某种操作; 
 }       
	对于自加的变量,一定要先进行初始化。
	p->data.Time++;
	对于循环中的一个自加变量,一定要注意是否多加了一次,或者少加了一次。
自加变量统计的是循环中的某种操作的次数,可以放操作前面,也可以放操作后面,但是放的位置不对,就有可能导致统计的错误。这种错误很难发现,可能导致数据输入输出的完全错误,甚至导致程序崩溃。
int i=1;
	while(i<=单词总数100)
	{
		输出序号,输出单词;
		i++;
	}
	cout<<"除去数字外,本文一共"<<i-1<<"个英文单词"<<endl;	
就比如这个循环,输出单词100个,i最终自加了100次,等于101;
	循环的条件很重要,循环了多少次,循环内的自加变量就自加多少。

	对于一个查找过程,可以设置一个标志变量flag
For/while(循环条件)
{	
		if(找到) 
		{	
			flag=1;
			break;
		}
}	
if(flag==1)
{
		输出查找成功;
}
else 	输出查找失败;

印象中,设置标志变量flag并break,可以避免某些查不出来的错误。

	数组作为函数参数
函数声明部分:
函数类型 函数名(数组类型 数组名[]);
Void OpenAddressing(HashTable HT[]);
函数调用部分:
函数名(数组名);
OpenAddressing(HT);

	Switch的格式

Char ch;
Cin>>ch;
switch(ch)
{
	 		case '1':MenuLinearList(L);break;
			case '2':LinkListSearch(L1);break;
			case '3':BinaryTreeSearch(T);break;
}
Break不能少。
Case后面是单引号。
	链地址法录入
void Input5(ALGraph &G)
{
	int i;
	//初始化顶点表 
	for(i=1;i<=26;i++)
	{
		G.Letter[i].letter_no=i;
		G.Letter[i].firstarc=NULL;
	}
	int num=0;//单词计数 
	for(i=0;i<t;i++)
	{
		int k;
		k=Hash_int(i);//判断单词首字母序号 
		WordNode *h,*w,*e,*p,*r;
		h=G.Letter[k].firstarc;//h是单词的首字母表头结点 
		if(h==NULL)//表头结点为空,录入 
		{
			p=new WordNode;
			p->data.key=str[i];
			p->data.Time=fre[i];
			p->nextarc=NULL;
			G.Letter[k].firstarc=p;
			num++;
		}
		else
		{
			int flag=0;
			while(h!=NULL)
			{
				r=h;
				if(h->data.key==str[i])//遍历一个首字母的链表,如果相同,频次自加 
				{
					h->data.Time++;
					flag=1;
				}
				h=h->nextarc;// 
			}
			if(flag==0)//如果没有相同的,就录入 
			{
				p=new WordNode;
				p->data.key=str[i];
				p->data.Time=fre[i];
				p->nextarc=NULL;
				r->nextarc=p;
				num++;
			}
		}
	}
	factnum=num;//有效单词数 
	return;
}
	核心代码
void Preprocessing()
{
	//读入 
	fstream in;
 	in.open("InFile.txt",ios::in);
 	if(!in)
 	{
  		cout<<"Can not open the file!"<<endl;
 		return;
    }
	//存入内存 
	int i=1;//从第一个单词开始,str[0]空置不用 
 	while(!in.eof())
 	{ 	
 		in>>str[i];
		int j=str[i].length();//单词字母数 
		//首字母大写转为小写 
		for(int n=0;n<j;n++)
		{
			if(str[i][n]>='A'&&str[i][n]<='Z')
					str[i][n]=str[i][n]+32;
		}
		
		//非字母字符标记统计
		int num=0; 
		for(int n=0;n<j;n++)
		{
			if(str[i][n]<'a'||str[i][n]>'z')
			{
				//cout<<"非字母:"<<str[i][n]<<endl;
				str[i][n]=35;
			//	cout<<"转化为:"<<str[i][n]<<endl;
				num++;
			}			
		}
		//非字母字符移动至字符串最后 
		for(int n=0;n<j;n++)
		{
			for(int k=0;k<j;k++)
				if(str[i][k]==35)
				{
					str[0][0]=str[i][k];
					str[i][k]=str[i][k+1];
					str[i][k+1]=str[0][0];
				}		
		}
		str[i]=str[i].substr(0,j-num);
		if(str[i][0]=='\0')
		{
			//cout<<"空格位置:"<<i<<endl;//system("pause");
			i--;//如果当前接收的是一个空串,则接受下一个字符串 
		}
		//统计单词数 
		i++;
	}
	t=i-1;//t为常量 
//	cout<<"单词总数:"<<t<<endl;
	
	//频率数组初始化 
	for(i=1;i<t;i++)
	{
			fre[i]=1;
	}
		
	return;
}
	单个函数内的非递归遍历,用一个while循环,不断改变根节点T的值
while(T!=NULL&&key!=T->data.key)//非递归遍历 
	{ 
		n++;
        if (key<T->data.key)
            T=T->lchild;
        else
            T=T->rchild;
    }

	常量设置
int t;//单词总数 
int factnum;//去重后单词数 
string str[MAXSIZE];//存放单词 
int fre[MAXSIZE];//存放词频
int flag[MAXSIZE];//标记第一次出现的位置 
double rate[MAXSIZE];
int setrate=100;//万分之100以下的单词过滤
int setrate2;



	结构体设置
typedef struct{
	string key;
	int Time;
}Word; 

typedef struct{
	Word *R;
	int length;
}SSTable;

typedef struct LNode
{
	Word data;
	struct LNode *next;
}LNode,*LinkList;

typedef struct BSTNode
{
 	Word data;
 	struct BSTNode *lchild,*rchild;
}BSTNode,*BSTree;

struct HashTable{
  	string  key;		         	 		
 	int  Time;				
};

typedef struct WordNode//单词结点 
{
	Word data;
	struct WordNode *nextarc;
}WordNode;

typedef struct LetterNode//首字母表头结点 
{
	int letter_no;
	WordNode *firstarc;
}LetterNode,AdjList[5000];//??

typedef struct
{
	AdjList Letter;//Letter
}ALGraph;//邻接表
	文件读入格式
fstream in;
 	in.open("InFile.txt",ios::in);
 	if(!in)
 	{
  		cout<<"Can not open the file!"<<endl;
 		return;
    }
	判断文件是否读取完毕
		while(!in.eof())
	写入文件
fstream out;
		out.open("OutFile1.txt",fstream::out | ios_base::trunc);
	时间函数
	#include <time.h>
	LARGE_INTEGER fr;
    LARGE_INTEGER st;
    LARGE_INTEGER ed;
    double time;
	QueryPerformanceFrequency(&fr);
    QueryPerformanceCounter(&st);
	QueryPerformanceCounter(&ed);
	time = (double)(ed.QuadPart - st.QuadPart) / (double)fr.QuadPart;

	#include <iomanip> 
格式控制符

	过滤空格
if(str[i][0]=='\0')
		{
			i--;//如果当前接收的是一个空串,则接受下一个字符串 
		} 
		i++;
	数据的初始化
//频率数组初始化 
for(i=1;i<t;i++)
{
	fre[i]=1;
}
//哈希表初始化 
	for(int i=1;i<MAXSIZE;i++)//不能有等于,越界就崩溃 
	{
			HT[i].key="\0";
			//cout<<i<<endl;
	}
	快速排序
void QuickSort1(SSTable &L, int l, int r)//l=1,r=L.length
{
    if (l < r)
    {
        //Swap(s[l], s[(l + r) / 2]); //将中间的这个数和第一个数交换 
        int i=l, j=r;Word x = L.R[l];
        while (i < j)
        {
            while(i < j && L.R[j].key>= x.key) // 从右向左找第一个小于x的数
                j--;  
            if(i < j) 
                L.R[i++] = L.R[j];
            
            while(i < j && L.R[i].key < x.key) // 从左向右找第一个大于等于x的数
                i++;  
            if(i < j) 
                L.R[j--] = L.R[i];
        }
        L.R[i] = x;
        QuickSort1(L, l, i - 1); // 递归调用 
        QuickSort1(L, i + 1, r);
    }
}
	从1开始的冒泡排序
	int i=0,j=0;//外i内j 
	for(i=1;i<=L.length;i++)//外层循环n-1 +1
		for(j=1;j<=L.length-i;j++)//内层循环n-1-i+1
	较高精度的概率(单位 万分之一)
10000*(double)L.R[i].Time/t
	哈希函数
int Hash(string key)
{
	int n;
	n=key[0]-'a';//用ASCII码作差,计算首字母的序号 
	int result=100*n;//每一个字母分配100个数组空间,a序号就是0,空间为1-100. 
	return result;
}
	对哈希表排序
void Sort4(HashTable HT[])
{
	int i=0;int j;int res=0;
	for(i=0;i<=26;i++)
	{
			for(j=i*100+1;j<(i+1)*100;j++)
			{
				if (HT[j].key=="\0")
				{
					res=j;
					//cout<<"i:"<<i*100+1<<" res:"<<res<<endl;
					break;
				}
			}
			QuickSort(HT,i*100+1,res);//(i+1)*100				
	}	
	return;	 
}

	文件流
头文件include<fstream>
定义文件流 fstream in
打开 in.open(“1.txt”,ios::in)
打开方式 ios::app 可以从上次读的地方接着读
while(!in.eof())没有读到文件的末尾,循环就会继续。
	String
#include<cstring>
str[i].length();
字符串可以直接比大小。
str[i][n]>='A'???
str[i][n]=str[i][n]+32;
str[i][n]=35;
截取字符串的函数
str[i].substr(0,j-num);
	菜单命令
system("pause");
system("cls");
	树的初始化
T=new BSTNode;
T=NULL;
	树的结构
typedef struct BSTNode
{
 	Word data;
 	struct BSTNode *lchild,*rchild;
}BSTNode,*BSTree;
BSTree 指针
开辟存储空间用BSTNode
	频次统计
首次录入设置频率为1。设为1是初始化,很重要。
再次录入时,查找数据结构,不录入,频率自加。
	Debug技巧 
当程序崩溃时,在程序的不同位置,cout相应的标志。
以检查崩溃的原因。没有输出结果,有以下几种常见原因:
死循环
死循环的原因,有函数层层嵌套导致的逻辑错误。
函数A调用函数B,但是函数B里又要调用函数	A。
未初始化
建立一个二叉树需要先初始化,先有init函数然后才能有create函数。
T=new BSTNode;
T=NULL;
指针越界
While(p->next)

3.对于多种数据结构,由2的图中数据可以分析出来,二叉排序树和顺序表的折半查找是最快的,平均查找长度也是最小的。

4.收获和体会
经过一个星期没日没夜的编程,我深刻的理解了码农一词的含义。
一天敲代码17个小时,大部分时间都是在debug。一个bug出现,有可能是逻辑错误,有可能是哪个符号或者是哪个语句的错误。编程的时候逻辑一定要清晰,最好能写出伪代码,这样能大大提高编程效率。而且一定要细心,任何一个微小的地方,都有可能导致整个程序的崩溃,比如if语句中的等号(==),不能写成赋值符号(=),最好是把数字写在前面(-1==res)。对于函数的命名,不要用相同的名字,最好安装菜单的序号,在具体的名字后加数字。用英文名,不要用拼音。命名应该规范。比如Menu类函数,menu应该放前面。对于编译器应该有更多的了解,比如devc++是可以支持c++11的,只需要在设置里改一下就行了。
发布了24 篇原创文章 · 获赞 5 · 访问量 2087

猜你喜欢

转载自blog.csdn.net/weixin_43673589/article/details/104151659
今日推荐