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

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

2015-12-28 18:23 701 查看
原帖http://blog.csdn.net/harry_lyc/article/details/7423326



1 Trie简介



Trie树,又称单词查找树键树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。

在本文中,对于输入的进行序列化,比如输入“单词查找树”,序列化为“单/词/查/找/树”,这样可以进行任何一种自定义的数据插入和查询。序列化输入可以根据自己的需要进行相应的改动,这样可以把Trie树结构应用到很多其他的语言和领域。

本Trie树结构的优点在于

1 不限制子节点的数量;

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

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

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

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

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


2 结构示意图



3 实现代码

Trie.h

[cpp] view
plaincopy

/********************************************************************

* 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.

*

* 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

[cpp] view
plaincopy

#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;

}

}

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