1 Trie简介
Trie树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。
在本文中,对于输入的进行序列化,比如输入“单词查找树”,序列化为“单/词/查/找/树”,这样可以进行任何一种自定义的数据插入和查询。序列化输入可以根据自己的需要进行相应的改动,这样可以把Trie树结构应用到很多其他的语言和领域。
本Trie树结构的优点在于:
1 不限制子节点的数量;
2 自定义的输入序列化,突破了具体语言、应用的限制,成为一个通用的框架;
3 可以进行最大Tokens序列长度的限制;
4 根据已定阈值输出重复的字符串;
5 提供单个字符串频度查找功能;
6 速度快,在两分钟内完成1998年1月份人民日报(19056行)的重复字符串抽取工作。
3 实现代码
Trie.h
/******************************************************************** * Copyright (C) 2012 Li Yachao * Contact: [email protected] or [email protected] * * Permission to use, copy, modify, and distribute this software for * any non-commercial purpose is hereby granted without fee, provided * that the above copyright notice appear in all copies and that both * that copyright notice. * It is provided "as is" without express or implied warranty. * * Version: 0.1 * Last update: 2012-4-2 *********************************************************************/ /********************************************************************* *********************************************************************/ #ifndef TRIE_H #define TRIE_H #include <iostream> #include <fstream> #include <string> #include <vector> #include <stdio.h> namespace MyUtility { /*用于存储原子数据的数据结构*/ typedef struct TrieNode { char* token;/*Trie节点的token值*/ bool terminal;/*当前节点是否是终结点*/ struct TrieNode* sons;/*子节点*/ struct TrieNode* next;/*兄弟节点*/ }TrieNode; /*输出结果的数据结构*/ typedef struct StrFreq { std::string Str;/*字符串*/ int Freq;/*频率*/ }StrFreq; class Trie { public: Trie() { CreateRoot(); travel_path.clear(); result.clear(); threshhold = 3; maxLength = 9 ; fout.open("result.txt"); } ~Trie() { Destroy(); } /*设置输出重复字符串频率的阈值*/ void SetThreshhold(int ts) { if(ts<=1) { return ; } threshhold = ts; } /*设置最长的字符串匹配长度的阈值*/ void SetMaxLength(int max_leng) { if(max_leng <= 1) { return ; } maxLength = max_leng; } /*输出结果*/ void Print(std::vector<StrFreq>& result); void Print(); bool AddString(const std::string& str); /*取得一个字符串的重复频率*/ int StrFrequency(const char* str); /*清空Trie树*/ bool Clear(); private: std::ofstream fout; TrieNode * Root;/*Trie树根节点*/ std::vector<std::string>travel_path;/*遍历是的访问路径*/ std::vector<StrFreq>result;/*重复字符串的输出结果*/ int sub_sons;/*一个节点的子节点数量*/ int threshhold;/*重复字符串输出阈值,默认为2*/ int maxLength;/*最长的Tokens序列长度,默认为9*/ void Tokenize(const std::string& str,std::vector<std::string>&vec_tokens); TrieNode * InsertNode(TrieNode* node,const char *token,bool end = false); /*查找一个节点是否有子节点值为token的节点,返回子节点的指针*/ TrieNode * FindNode(TrieNode* p_node,const char *token); /*初始化一个新的Trie节点*/ inline TrieNode* NewNode() { TrieNode * newNode = new TrieNode(); newNode->sons = NULL; newNode->next = NULL; newNode->token = NULL; newNode->terminal = false; return newNode; } /*初始化一个新的Trie树根节点*/ void CreateRoot() { if( NULL != Root) { delete Root; Root = NULL; } Root = NewNode(); char * root_tag ="Root"; Root->token = new char[sizeof(char)*strlen(root_tag)]; strcpy(Root->token,root_tag); } /*销毁Trie树*/ void Destroy(); /*销毁Trie子树*/ void Destroy(TrieNode * node); /*遍历树结构*/ void Travel(TrieNode* node); /*取得一个节点的子节点数*/ void TrieNodeSons(const TrieNode* node); void TrieNodeSons(const TrieNode* node,const TrieNode* root); }; } #endifTrie.cpp
#include "Trie.h" namespace MyUtility { /* ************************************************* 功能 : 中文文本预处理,序列化输入 参数 : 返回值 : ------------------------------------------------- 备注 : ------------------------------------------------- 作者 :Li Yachao 时间 :2012-4-3 ************************************************* */ void Trie::Tokenize(const std::string &str, std::vector<std::string> &vec_tokens) { vec_tokens.clear(); std::string tmp =""; if(str.empty()) { return ; } for(int i=0;i<str.size();i++) { unsigned char c = str[i]; if(c < 128) { tmp = str.substr(i,1); vec_tokens.push_back(tmp); } else { tmp = str.substr(i,2); vec_tokens.push_back(tmp); i++; } } } /* ************************************************* 功能 : 销毁Trie树 参数 : 返回值 : ------------------------------------------------- 备注 : ------------------------------------------------- 作者 :Li Yachao 时间 :2012-4-3 ************************************************* */ void Trie::Destroy() { Destroy(Root); } void Trie::Destroy(TrieNode * node) { if(NULL != node) { Destroy(node->sons); Destroy(node->next); delete node; node = NULL; } else { return ; } } /* ************************************************* 功能 : 清空Trie树 参数 : 返回值 : ------------------------------------------------- 备注 : ------------------------------------------------- 作者 :Li Yachao 时间 :2012-4-3 ************************************************* */ bool Trie::Clear() { Destroy(); CreateRoot(); travel_path.clear(); result.clear(); return true; } /* ************************************************* 功能 : 取得一个Trie数节点的子节点数,即一个Token序列的重复次数。 参数 : 返回值 : ------------------------------------------------- 备注 : ------------------------------------------------- 作者 :Li Yachao 时间 :2012-4-3 ************************************************* */ void Trie::TrieNodeSons(const TrieNode * node,const TrieNode* root) { if(NULL != node) { TrieNodeSons(node->sons,root); if(node->terminal) { sub_sons++;/*以Token序列是否是序列结尾为标志*/ } if(node != root) {/*根节点不能遍历其兄弟节点*/ TrieNodeSons(node->next,root); } } else { return ; } } /*void Trie::TrieNodeSons(const TrieNode * node) { if(NULL != node) { TrieNodeSons(node->sons); if(node->terminal) { sub_sons++; } if(node != Root) { TrieNodeSons(node->next); } } else { return ; } }*/ /* ************************************************* 功能 : 遍历Trie数所有的节点,根据设定的threashold输出Token序列 参数 : 返回值 : ------------------------------------------------- 备注 : ------------------------------------------------- 作者 :Li Yachao 时间 :2012-4-3 ************************************************* */ void Trie::Travel(TrieNode* node) { if(NULL != node) { if(node != Root) travel_path.push_back(node->token); Travel(node->sons); /********************************************************/ sub_sons =0; //TrieNodeSons(node); TrieNodeSons(node,node); int sum = sub_sons; //sub_sons = 0; //TrieNodeSons(node->sons,node->sons); if((sub_sons >= threshhold)) //if((sum != sub_sons) && (sum >= threshhold)) { std::string buf=""; for(int i=0;i<travel_path.size();i++) { buf += travel_path[i]; } if(!buf.empty()) { //fout<<buf<<"\t"<<sum<<std::endl; fout<<buf<<"\t"<<sub_sons<<std::endl; } } if(travel_path.size() > 0) { travel_path.pop_back(); } /********************************************************/ Travel(node->next); /********************************************************/ /********************************************************/ } else { return ; } } void Trie::Print() { travel_path.clear(); result.clear(); Travel(Root); std::cout<<"String\tFrequency"<<std::endl; for(int i=0;i<result.size();i++) { std::cout<<result[i].Str <<"\t"<<result[i].Freq <<std::endl; } result.clear(); } void Trie::Print(std::vector<StrFreq>& re_result) { travel_path.clear(); result.clear(); Travel(Root); //re_result = result; //result.clear(); } /* ************************************************* 功能 : 输入Trie树字符串 参数 : 返回值 : ------------------------------------------------- 备注 : ------------------------------------------------- 作者 :Li Yachao 时间 :2012-4-3 ************************************************* */ bool Trie::AddString(const std::string &str) { std::vector<std::string>val; std::vector<std::string>val_tmp; Tokenize(str,val); int step = maxLength; for(int i=0;i<val.size();i++) { val_tmp.clear(); while((i+step) > val.size()) { step --; } for(int j=i;j< (i+step);j++) { val_tmp.push_back(val[j]); } TrieNode* cur = Root; for(int j=0;j<val_tmp.size();j++) { if(j == val_tmp.size() - 1) { InsertNode(cur,val_tmp[j].c_str(),true); } else { cur = InsertNode(cur,val_tmp[j].c_str()); } } } return true; } /* ************************************************* 功能 : 插入Trie树节点 参数 : 返回值 : 插入节点的指针 ------------------------------------------------- 备注 : ------------------------------------------------- 作者 :Li Yachao 时间 :2012-4-3 ************************************************* */ TrieNode * Trie::InsertNode(TrieNode* node,const char *token,bool end) { if(NULL == node) { return NULL; } if(NULL == node->sons) { node->sons = NewNode(); node->sons->token = new char[sizeof(char)*strlen(token)]; strcpy(node->sons->token,token); if(end) { node->sons->terminal = true; } return node->sons; } else { TrieNode* cur = node->sons; while(NULL != cur->next) { if(strcmp(cur->token,token) == 0) { if(end) { cur->terminal = true; } return cur ; } if( NULL != cur->next) { cur = cur->next ; } else { break; } } if(strcmp(cur->token ,token) == 0) { if(end) { cur->terminal = true; } return cur; } TrieNode* n = NewNode(); n->token = new char[sizeof(char)*strlen(token)]; strcpy(n->token ,token); if(end) { n->terminal = true; } cur->next = n; return n; } } /* ************************************************* 功能 : 查找一个字符串的重复次数 参数 : 返回值 : ------------------------------------------------- 备注 : ------------------------------------------------- 作者 :Li Yachao 时间 :2012-4-3 ************************************************* */ int Trie::StrFrequency(const char* str) { std::vector<std::string>tokens; Tokenize(str,tokens); TrieNode * cur = Root; for(int i=0;i<tokens.size();i++) { cur = FindNode(cur,tokens[i].c_str()); } if(NULL == cur) { return 0; } else { sub_sons =0; TrieNodeSons(cur, cur); return sub_sons; } } /* ************************************************* 功能 : 查找一个节点的指针 参数 : 返回值 : ------------------------------------------------- 备注 : ------------------------------------------------- 作者 :Li Yachao 时间 :2012-4-3 ************************************************* */ TrieNode * Trie::FindNode(TrieNode *p_node, const char *token) { if((NULL != p_node) && (NULL != p_node->sons)) { TrieNode *cur = p_node->sons; while(NULL != cur) { if(strcmp(cur->token,token) == 0) { return cur; } cur = cur->next; } return NULL; } else { return NULL; } } }