1.需求描述
1.1 问题描述
设计数据结构完成在一个文档集合的存储,并构造算法实现其内容的查询。该设计包括三个部分:
①应用数据结构完成文档集合的内容(基于单词的)存储,并为下一步的查询建立索引。
②就单个单词的查询请求,设计算法进行查询。
③对多个单词通过AND和OR构造的复杂查询进行处理(此处可只做两个单词的情况)。
1.2 基本要求
① 文档集合中的文档数不能少于20个
② 数据结构的设计以及查找算法的构造应考虑如何最大程度的提高查询效率
③ 查询效率的提高应是综合多种查询的,而不是只针对一种查询的优化。
④ 给出查询效率的模拟实验数据。
1.3输入要求
①查找一个单词时,输入单词
②查找多个单词的and、or时,输入查找数量和单词
输入形式
首先根据提示选择要进行的操作
- 单个单词
- 单词数量和单词
输入数据例子
例如选择的操作是查找单个单词,要查找的单词为data,则输入:data
例如选择的操作是查找多个单词的与,查找的数量为4,则输入:4,并根据提示依次输入4个要查找的单词
1.4输出要求
①输出文档内容
②输出单词所在文件名
输出形式
- 文档内容
- 文件名
输出数据例子
例如,输出0号文件内容为:I like the class on data structures and algorithms
例如,单词data所在的文件有:eight.txt
2.设计
2.1 结构设计
程序主要功能模块
- 欢迎模块:开始显示欢迎界面
- 文件读入模块:
①读取文档模块:将指定文件夹内文档读出
②存储文档模块:将读出的文档内容写入系统
- 索引模板:
①建立索引模块:建立单词位置索引
②根据索引查找模块:
A.查找一个单词模块:查找一个单词所在文件名
B.多单词“与”模块:查找包含多个单词的文件
C.多单词“或”模块:查找多个单词文件的总和
- 输出模块:输出指定文件内容
模块设计
①欢迎模块hello设计
·文字输出
·用户选择要进行的操作
②文件读入模块设计
A.读取文档模块设计
·获取用户指定文件夹所有的文档名,返回给用户文档数量
·根据文档名依次打开文件并读取内容
B.存储文档模块设计
·将读取内容存入自定义结构txt中,并返回给用户文档编号、长度
·所有txt组成自定义文档集合group
③索引模块Index设计
A.建立索引模块设计
·自定义结构Ptn存储单词和单词所在的文档编号,然后根据单词首字母对应ascll码作为key值投影到哈希表中
·使用链式散列解决冲突溢出问题
B.根据索引查找模块设计
a.查找一个单词模块设计
·用户输入单词,根据其首字母和散列函数找到对应自定义链表wordChain,遍历链表找到包含单词的Ptn结构,返回Ptn结构中的文档编号
b.多个单词“与”模块设计
·用户输入要查找的多个单词,分别对单个单词查找,在得到的多组文档编号中,查找重复出现的编号
c.多个单词“或”模块设计
·用户输入要查找的多个单词,分别对单个单词查找,将得到的多组文档编号合并,去重,得到总和
④输出模块设计
·用户输入文档编号,输出该文档内容
2.2 数据及数据类(型)定义
- 用线性表存储文档
·class txt:len文档长度;name文档名称;word[]文档内容
·class group:txt list[]存储文档;lab文档标号;chain索引哈希表; ge读取文档
- struct Ptn:str存储单词;pos[]单词所在文档
- class Getname:封装读取文件的方法
- struct wordNode:element元素是Ptn
- class wordChain:dSize链表长度
- class hashtable:*table散列表;dsize元素个数;divisor桶数
2.3.算法设计及分析
(各模块算法及类内函数的算法伪码表示)
①class txt
print()数组的遍历输出
②类wordChain
A.插入到链表中Insert(Ptn *thePtn)
if(头节点为空){
Ptn构建新节点;
头节点为新节点;
链表长度增加;}
else{
遍历链表
if(当前节点内单词与插入Ptn内单词相同)
将插入Ptn的数组元素更新到当前节点
else
构建新节点并插入到链表
}
B.判断是否存在单词findp(string theStr)
if(链表长度为0) return false
else
遍历链表
if(当前节点内单词与所查单词相同)
return true
if(当前节点为NULL) return false
C.查找单词所在位置,返回Ptn find(string theStr)
遍历链表
if(当前节点内单词与string相同)
返回节点存储的Ptn
③类hashtable
wordChain table[52];对应52条链表
A.插入到链表中Insert(Ptn *thePtn)
根据单词首字母确定桶homeBucket号
table[homeBucket].insert(thePtn)
B.判断是否存在单词findp(string theStr)
根据theStr首字母确定桶号
return table[桶号].findp(theStr)
C.查找单词所在位置,返回Ptn find(string theStr)
根据theStr首字母确定桶号
return table[桶号].find(theStr)
④类Getname
A.获取指定路径文件夹内的文件名getFiles(string path,vector<string>& files)
if(打开path是路径)
迭代到下一层
else
加入列表
B.将读取到的文件名存入线性表into()
Vector<string> files;
getFile(路径,files);
file容器内文件名复制到数组
⑤类group
Txt list[30];
Getname ge;
hashChain chain;
A.导入文件newdoc()
ge.into();获取存文件名的数组
遍历该数组{
建立txt
根据文件名,读取文件内容,存入txt内的数组
遍历txt内数组{
新建Ptn,Ptn.str为当前单词,Ptn.pos[0]=文档txt标号
chain.insert(Ptn)}
list[lab]=txt;存入list线性表
lab标号加1}
B.输出文件outdoc(index)
list[index].print();
C.查找单词的位置 findpos(string thestr)
if(chain.findp(thestr)){
Ptn *tep=chain.find(thestr);
遍历tep内数组,并输出}
else
没有该单词
D.数组去重qc(int *x,int number)
E.多个单词的或Orfind2(int huo)
输入huo个单词
通过chain.find()获取每个单词的数组,并全部存入的临时数组
对临时数组去重
遍历输出去重后数组
F.多个单词的与Andfind2(int su2)
输入su2个单词
建立两个临时数组
通过chain.find()获取每个单词的数组
临时数组1内为第一个单词的数组
第n(双数)个单词的数组通过二次遍历找到与临时数组1相同的元素,存入 临时数组2
第m(单数)个单词的数组通过二次遍历找到与临时数组2相同的元素,存入 临时数组1
if(su2为单数)输出临时数组1
if(su2为双数)输出临时数组2
3. 测试结果
①默认读入指定文件夹的内容,每读入一个文档会返回该文档的名称,分配的编号,以及文档长度,入下图
②输出指定文件,该操作要根据文档默认读入时分配的编号进行,输入编号,输出对应文档的内容,例如,想要输出文档“nineteen.txt”,输入其对应编号9
如果该文件不存在,例如输入没有的24号
③查找单个单词所在的文档,例如单词other
如果这个单词不存在,例如neddho
④查找包含所有指定单词的文件,例如:data,is,and,the
如果有单词不存在,例如:data,is,and,nedd
如果不存在共同文档,例如:family,love,need
⑤查找所有单词所在文件的总和,例如:family,need,has,you
如果有单词不存在,例如power,always,kkkll,you,all
4. 分析与探讨
- 思路
该系统的主要思路为:首先将指定文件夹内文档的内容存储到自定义类中,然后根据自定义类中的文档内容建立索引,并根据索引实现一系列的查找操作。
- 测试
该程序默认读入了指定路径文件夹的内容,3中测试结果,如果输入有误,将会根据不同的错误进行提示。
- 调试优化过程
A.文档读入模块:
·第一步只实现了从命令行几个输入文档内容
·第二步添加了读取文件的模块,实现了输入文档名,将文档内容存入自定义类中
·第三步添加了扫描文件夹内文档的操作,实现了输入整个文件夹的文档,并将文档内容存入自定义类中
B.根据索引查找模块
·第一步实现了查找单个单词,查找两个单词的与、或,采用的是低级的两个数组之间查找重复元素和两个数组合并后去重的操作
·第二步实现了查找多个单词的与、或,应用多次循环嵌套,自定义数组去重方法