實現功能
初始化文檔(默認文檔中全是小寫字母,並且無重複單詞),將內部單詞存入字典樹(每行一個單詞),實現以下功能:
- 查找單詞(輸出單詞行號)
- 查找前綴(輸出前綴行號們)
- 刪除單詞(在字典樹中刪除單詞,文檔中原始結構不變)
- 插入單詞(在字典樹中插入單詞,原始文檔結構不變,默認行號是最後一行)
- 按序輸出單詞(輸出仍然在字典樹中的單詞,按照字典序)
結構
字典樹使用鏈表與數組結合的方式實現,父子關係使用鏈表,兒子節點之間的關係使用數組。
注意,刪除操作只是在字典樹中刪除對應的單詞,其他單詞在原始文檔中對應的行號不變。
完整代碼
/*
* 實驗名稱: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;
}