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

1 Trie简介

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

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

        本Trie树结构的优点在于

       1 不限制子节点的数量;

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

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

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

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

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

2 结构示意图

一个通用的Trie树,标准C++实现_第1张图片

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);
	};

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



你可能感兴趣的:(数据结构,C++,String,null,token,tokenize)