实现功能
初始化文档(默认文档中全是小写字母,并且无重复单词),将内部单词存入字典树(每行一个单词),实现以下功能:
- 查找单词(输出单词行号)
- 查找前缀(输出前缀行号们)
- 删除单词(在字典树中删除单词,文档中原始结构不变)
- 插入单词(在字典树中插入单词,原始文档结构不变,默认行号是最后一行)
- 按序输出单词(输出仍然在字典树中的单词,按照字典序)
结构
字典树使用链表与数组结合的方式实现,父子关系使用链表,儿子节点之间的关系使用数组。
注意,删除操作只是在字典树中删除对应的单词,其他单词在原始文档中对应的行号不变。
完整代码
/*
* 实验名称:N-ary Trie
* 作者:龙征天
* 环境:MacOs
* 编译器:CLion
*/
//#pragma GCC optimize(2)
//#pragma G++ optimize(2)
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <climits>
#include <algorithm>
#include <queue>
#include <vector>
#include <fstream>
using namespace std;
struct TreeNode{
TreeNode(){
rows.clear();
endrow=0;
for (int i=0; i<26; i++) child[i]=nullptr;
}
TreeNode* child[26];//固定26个孩子元素
vector<int> rows;
int endrow;
};
class LinkedTree{
public:
LinkedTree(){ root=new TreeNode; rows=0; }
~LinkedTree(){}
void init(const string& _file);
int getRows(){return rows;};
TreeNode* getRoot(){return root;};
void insert(const string& _s,int _row);//插入一个单词
bool erase(const string& _s);//删除一个单词
void dfserase(const string& _s,int index, TreeNode* &a, int delrow);//返回值表示类型,1为分支类型,2为前缀类型
int FindWord(const string& _s);//查找完整单词的行号
vector<int> FindPrefix(const string& _s);//查找前缀的行号(包含单词)
void traverse(TreeNode *node,string _s,int index);
private:
TreeNode *root;
int rows;
};
void LinkedTree::init(const string& _file){
ifstream fin(_file);
if(!fin.is_open()){
perror("Error: "); exit(0);
}
string s;
while(fin>>s) insert(s,++rows);
printf("初始化成功!\n");
}
void LinkedTree::insert(const string& _s,int _row){
TreeNode *currentNode=root;
for (int i=0; i<_s.size(); i++){
if(currentNode->child[_s[i]-'a']==nullptr) currentNode->child[_s[i]-'a']=new TreeNode;
currentNode->child[_s[i]-'a']->rows.push_back(_row);
currentNode=currentNode->child[_s[i]-'a'];
}
currentNode->endrow=_row;
}
int LinkedTree::FindWord(const string& _s){
TreeNode *currentNode=root;
for (int i=0; i<_s.size(); i++){
if(currentNode->child[_s[i]-'a']==nullptr) return -1;
currentNode=currentNode->child[_s[i]-'a'];
}
if(currentNode->endrow) return currentNode->endrow;
else return -1;
}
vector<int> LinkedTree::FindPrefix(const string& _s){
TreeNode *currentNode=root;
for (int i=0; i<_s.size(); i++){
if(currentNode->child[_s[i]-'a']==nullptr) return vector<int>{-1};
currentNode=currentNode->child[_s[i]-'a'];
}
return currentNode->rows;
}
void LinkedTree::dfserase(const string &_s, int index, TreeNode* &a, int delrow){//最后一个参数要删除的行号
if(index==_s.size()-1) {
auto it=find(a->rows.begin(),a->rows.end(),delrow);
if(it!=a->rows.end()) a->rows.erase(it);
a->endrow=0;
return;
}
dfserase(_s,index+1,a->child[_s[index+1]-'a'],delrow);
if(a->child[_s[index+1]-'a']->rows.empty()){//当前节点的儿子没有儿子,这是分支点
delete a->child[_s[index+1]-'a'];
a->child[_s[index+1]-'a']=nullptr;
auto it=find(a->rows.begin(),a->rows.end(),delrow);
if(it!=a->rows.end()) a->rows.erase(it);
//if(a->endrow==delrow) a->endrow=0;
return;
}
else {//进入前缀情况
auto it=find(a->child[_s[index+1]-'a']->rows.begin(),a->child[_s[index+1]-'a']->rows.end(),delrow);
if(it!=a->child[_s[index+1]-'a']->rows.end()) a->child[_s[index+1]-'a']->rows.erase(it);
//if(a->child[_s[index+1]-'a']->endrow==delrow) a->child[_s[index+1]-'a']->endrow=0;
return;
}
}
bool LinkedTree::erase(const string& _s){
int temp=FindWord(_s);
if(temp==-1) return false;//如果不存在这个字符串,返回false
dfserase(_s,-1,root,temp);
// auto it=find(root->child[_s[0]-'a']->rows.begin(),root->child[_s[0]-'a']->rows.end(),temp);
// if(it!=root->child[_s[0]-'a']->rows.end()) root->child[_s[0]-'a']->rows.erase(it);
return true;
}
void LinkedTree::traverse(TreeNode *node,string _s,int index){//测试用,遍历整棵树
if(node->endrow){
for (int i=0; i<index; i++) printf("%c",_s[i]);
printf("\n");
}
for (int i=0; i<26; i++){
if(node->child[i]!=nullptr){
_s[index]=char(i+'a');
traverse(node->child[i],_s,index+1);
}
}
}
int main(){
LinkedTree trieTree;
printf("请输入您想要索引化的文档目录绝对路径:");
string file; cin>>file;
trieTree.init(file);//初始化
while(true){
printf("请输入需要进行的操作:1表示查找前缀,2表示查找单词,3表示插入新的单词,4表示删除单词,5表示按序输出单词,6表示退出程序。\n");
printf("请输入:");
int op; scanf("%d",&op);
if(op==1){
printf("请输入你想要在字典树中查找的前缀:");
string s; cin>>s;
vector<int> tempvector=trieTree.FindPrefix(s);
if(tempvector[0]==-1) printf("不存在此前缀!\n");
else {
printf("出现此前缀的单词所在的行号为:");
for (auto i:tempvector) printf("%d ",i);
printf("\n");
}
}
else if(op==2){
printf("请输入你想要在字典树中查找的单词:");
string s; cin>>s;
int x=trieTree.FindWord(s);
if(x==-1) printf("不存在此单词!\n");
else printf("此单词出现的行号为:%d\n",x);
}
else if(op==3){
printf("请输入你想要在字典树中插入的单词,默认行号为最后一行:");
string s; cin>>s;
trieTree.insert(s,trieTree.getRows());
printf("插入成功!\n");
}
else if(op==4){
printf("请输入你想要在字典树中删除的单词,注意,删除后,源文件中仍然存在,请输入:");
string s; cin>>s;
if(trieTree.erase(s)) printf("删除成功!\n");
else printf("删除失败!\n");
}
else if(op==5) {
string s; s.clear();
trieTree.traverse(trieTree.getRoot(),s,0);
}
else if(op==6) {
printf("谢谢使用!再见!\n"); break;
}
}
return 0;
}