您的位置:首页 > 编程语言 > C语言/C++

一个通用的Trie树,标准C++实现

2012-05-08 17:13 281 查看
Trie简介



Trie树,又称单词查找树键树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。
[align=center][/align]
在本文中,对于输入的进行序列化,比如输入“单词查找树”,序列化为“单/词/查/找/树”,这样可以进行任何一种自定义的数据插入和查询。序列化输入可以根据自己的需要进行相应的改动,这样可以把Trie树结构应用到很多其他的语言和领域。

本Trie树结构的优点在于

1 不限制子节点的数量;

2 自定义的输入序列化,突破了具体语言、应用的限制,成为一个通用的框架;

3 可以进行最大Tokens序列长度的限制;

4 根据已定阈值输出重复的字符串;

5 提供单个字符串频度查找功能;

6 速度快,在两分钟内完成1998年1月份人民日报(19056行)的重复字符串抽取工作。


2 结构示意图



3 实现代码

Trie.h

/********************************************************************
* Copyright (C) 2012 Li Yachao
* Contact: liyc7711@gmail.com or harry_lyc@foxmail.com
*
* 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.
* http://www.linuxidc.com * 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);
};

}
#endif

Trie.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;
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: