一、题目
1.问题描述
设计数据结构完成在一个文档集合的存储,并构造算法实现其内容的查询。该设计包括三个部分:
① 应用数据结构完成文档集合的内容(基于单词的)存储,并为下一步的查询建立索引。
② 就单个单词的查询请求,设计算法进行查询。
③ 对多个单词通过AND和OR构造的复杂查询进行处理(此处可只做两个单词的情况)。
具体情形如下面的例子:
Doc1:I like the class on data structuresand algorithms.
Doc2:I hate the class on data structuresand algorithms.
Doc3:Interesting statistical data mayresult from this survey.
Here are theanswers to some queries:
Query 1:data
Doc1,Doc2,Doc3
Query 2:data AND structures
Doc1,Doc2
Query 3:like OR survey
Doc1,Doc3
2.基本要求:
① 文档集合中的文档数不能少于20个
② 数据结构的设计以及查找算法的构造应考虑如何最大程度的提高查询效率
③ 查询效率的提高应是综合多种查询的,而不是只针对一种查询的优化。
④ 给出查询效率的模拟实验数据。
3.实验提示:
AND和OR查询可转变为单个单词查询结果的组合
二、 设计
1. 设计结构
建立文档结构,将文档内容存储在线性表中,该文档结构组成线性表为文档集合,建立哈希表作为索引,字典结构由单词和该单词所在文件编号组成的数组构成。
2. 数据及数据类(型)定义
① 文档类txt,单个文档,内容存入string类型的线性表中
② 文档集合group,txt类型的线性表
③ Ptn类,key值是string类型的单词首字母,value是该单词所在的位置组成的数组
④ wordNode链表节点,element元素是Ptn
⑤ wordChain链表,存储Ptn
⑥ hashtable建立索引
① 输入文档
创建txt;
输入文档内容;
每输入一个单词,建立Ptn结构,对应数组0号位存文档编号
将Ptn插入hashtable
② Ptn插入hashtable
Ptn中单词首字母,确定所在hashtable第几条链表
插入链表
③ 查找位置
Hashtable中查找Ptn
找到对应链表
遍历链表的Ptn
返回数组
三、实现
1.文档结构txt
#include<iostream>
#include<string>
using namespace std;
//文档类
class txt{
public:
txt(){};//普通构造函数
void add();//输入文件
void print();//输出文件
int len;//文件长度
string word[1000];//存储文件的数组
};
void txt::add(){
cout<<"你的文件长度是?"<<endl;
cin>>len;
if(len<0){
cout<<"文件长度应大于0!"<<endl;
}
else{
cout<<"请输入!"<<endl;
for(int i=0;i<len;i++){
cin>>word[i];
}
}
}
void txt::print(){
cout<<"您的文件内容如下:"<<endl;
for(int i=0;i<len;i++){
cout<<word[i]<<" ";
}
}
2.Ptn结构
struct Ptn{
string str;//存的单词
int pos[30];//单词所在的数组
int psize;//数组长度
Ptn(){psize=1;} //该结构类比于数对,其中first对应str,second对应pos
void outpos(){//输出数组
for(int i=0;i<psize;i++){
cout<<pos[i]<<",";
}
cout<<"号文件!";
return;
}
void Andpos(const Ptn& d){//两个数组并,输出
int andpos[30];
int andsize=0;
for(int i=0;i<psize;i++){
for(int p=0;p<d.psize;p++){
if(pos[i]==d.pos[p]){
andpos[andsize]=pos[i];
andsize++;}
}
}
cout<<str<<"和"<<d.str<<"同时出现的文档有"<<andsize<<"个,分别是:"<<endl;
for(int i=0;i<andsize;i++){
cout<<andpos[i]<<",";
}
cout<<"号文件!";
}
void Orpos(const Ptn& d){//两个数组或,输出
int orpos1[30];
int orpos[30];
int orsize1=0;
int orsize=0;
for(int i=0;i<psize;i++){
orpos1[orsize1]=pos[i];
orsize1++;
}
for(int i=0;i<d.psize;i++){
orpos1[orsize1]=d.pos[i];
orsize1++;
}
for(int i=0;i<orsize1;i++){
for(int p=i+1;p<orsize1;p++){
if(orpos1[i]==orpos1[p]){
orpos1[p]=999999;
}
}
}
for(int i=0;i<orsize1;i++){
if(orpos1[i]!=999999){
orpos[orsize]=orpos1[i];
orsize++;}
}
cout<<str<<"和"<<d.str<<"一共出现的文档有"<<orsize<<"个,分别是:"<<endl;
for(int i=0;i<orsize;i++){
cout<<orpos[i]<<",";
}
cout<<"号文件!";
}
void operator=(const Ptn& d){
//赋值运算符重载
str=d.str;
psize=d.psize;
memcpy(pos,d.pos,sizeof(d.pos));//数组复制
}
};
3.链表wordChain
struct wordNode{//链表节点
//数据组成
Ptn element;//数据是Ptn形式
wordNode *next;//链表指针
//构造方法
wordNode(){ }//该结构为链表节点
wordNode(const Ptn& element){
this->element=element;
this->next=NULL;
}
wordNode(const Ptn& element,wordNode* next){
this->element=element;
this->next=next;
}
};
class wordChain{//链表类
public:
wordChain(){
firstNode=NULL;
dSize=0;//链表长度
}//构造方法
bool empty() const{return dSize==0;}//判断链表是否为空
int size()const{return dSize;} //返回链表长度
Ptn* find(const string&) const;//根据单词寻找,返回Ptn指针类型
void erase(const string&);//根据单词删除
void insert(const Ptn&);//插入Ptn
void operator=(const wordChain& d){
//赋值运算符重载
firstNode=d.firstNode;
dSize=d.dSize;
}
protected:
wordNode* firstNode;//头节点
int dSize;//链表长度
};
Ptn* wordChain::find(const string& theStr)const{
//根据string单词在链表中找到Ptn数对
wordNode* currentNode=firstNode;
while(currentNode!=NULL&&
currentNode->element.str!=theStr)
currentNode=currentNode->next;//如果不是,则向后寻找
// if(currentNode->element.str==theStr){
if(currentNode!=NULL&¤tNode->element.str==theStr){
// cout<<"等于"<<endl;
return ¤tNode->element;//如果找到,则返回element,即Ptn类型
}
return NULL;
}
void wordChain::insert(const Ptn& thePtn){//提供element,插入新的节点到链表
if(firstNode==NULL){//头节点为空
wordNode *newNode=new wordNode(thePtn);//构建新节点
firstNode=newNode;//新节点作为头节点
dSize++;
}
else{//头节点不空
wordNode *p=firstNode;
while(p->next!=NULL&&p->element.str!=thePtn.str){
p=p->next;
}
if(p->element.str==thePtn.str){
p->element.pos[p->element.psize]=thePtn.pos[0];
p->element.psize++;
}
else {
wordNode *newNode=new wordNode(thePtn);
p->next=newNode;
dSize++; }
}
return;
}
void wordChain::erase(const string& theStr){
//根据所给单词,在链表中删除节点
wordNode *deleteNode;
if(firstNode->element.str==theStr){
deleteNode=firstNode;
firstNode=firstNode->next;
} //头节点是要删除的
else{
wordNode *p=firstNode;
int reco=0;
while(reco<dSize){
if(p->element.str==theStr){
deleteNode=p;
p=p->next;
}
else
p=p->next;
reco++;
}
}//找到节点并删除
dSize--;
}
4.hashtable
class hashChains{//哈希表
public:
hashChains(int theDivisor=52){
divisor=theDivisor;
dsize=0;
table=new wordChain[divisor];//52个链表,每个链表对应单词的首字母,共52个
}
bool empty()const{return dsize==0;}
int hashsize()const{return dsize;}
Ptn* find(const string& theStr)const{
//根据单词找到位置
char now=theStr[0];//要找的单词的首字母
char mi;
if(now>96){
mi=now-6;
}
else{//大写字母
mi=now;
}
return table[mi%divisor].find(theStr);
}
void insert(const Ptn& thePtn){
//插入一个数对形式
char in=(thePtn.str)[0];//首字母
int homeBucket;
if(in>96){//小写字母
homeBucket=(in-6)%divisor;
}
else{//大写字母
homeBucket=in%divisor;
}
int homeSize=table[homeBucket].size();
table[homeBucket].insert(thePtn);
if(table[homeBucket].size()>homeSize)
dsize++;//说明插入成功,dsize++
}
protected:
wordChain *table;//散列表
int dsize;//元素个数
int divisor;//桶数
};
5.应用程序
#include<iostream>
#include<string>
#include"GROUP.h"
int main(){
cout<<"欢迎来到文档存储系统!"<<endl;
group gr=group(0);
cout<<"您是否需要立刻创建文档集合?(Y/N)"<<endl;
char first;
cin>>first;
if(first=='Y'){
for(int i=0;i<30;i++){
cout<<"您是否需要存储文件?(Y/N)"<<endl;
char sec;
cin>>sec;
if(sec=='Y'){
gr.newdoc();
}
else
break;
}
}
cout<<"存储完毕!"<<endl;
for(int i=0;i<30;i++){
cout<<"请选择接下来的操作:A.输出文件 B.查找某个单词的位置 C.查找两个单词都出现的位置 D.查找两个单词出现位置的总和 E.退出系统"<<endl;
char thr;
cin>>thr;
if(thr=='A'){
int ou;
cout<<"您要输出的文件编号是:";
cin>>ou;
gr.outdoc(ou);
cout<<endl;
}
if(thr=='B'){
string si;
cout<<"您要查找的单词是:";
cin>>si;
gr.findpos(si);
cout<<endl;
}
if(thr=='C'){
string s1,s2;
cout<<"您要查找的第一个单词是:";
cin>>s1;
cout<<"第二个单词是:";
cin>>s2;
gr.Andfind(s1,s2);
cout<<endl;
}
if(thr=='D'){
string s1,s2;
cout<<"您要查找的第一个单词是:";
cin>>s1;
cout<<"第二个单词是:";
cin>>s2;
gr.Orfind(s1,s2);
cout<<endl;
}
if(thr=='E'){
cout<<"正在退出,再见!";
break;}
}
}
四、测试结果
这是初步的实现,具体的完善优化在(二)中详细介绍。