1 Trie簡介
Trie樹,又稱單詞查找樹或鍵樹,是一種樹形結構,是一種哈希樹的變種。典型應用是用於統計和排序大量的字符串(但不僅限於字符串),所以經常被搜索引擎系統用於文本詞頻統計。它的優點是:最大限度地減少無謂的字符串比較,查詢效率比哈希表高。
在本文中,對於輸入的進行序列化,比如輸入“單詞查找樹”,序列化為“單/詞/查/找/樹”,這樣可以進行任何一種自定義的數據插入和查詢。序列化輸入可以根據自己的需要進行相應的改動,這樣可以把Trie樹結構應用到很多其他的語言和領域。
本Trie樹結構的優點在於:
1 不限制子節點的數量;
2 自定義的輸入序列化,突破了具體語言、應用的限制,成為一個通用的框架;
3 可以進行最大Tokens序列長度的限制;
4 根據已定阈值輸出重復的字符串;
5 提供單個字符串頻度查找功能;
6 速度快,在兩分鐘內完成1998年1月份人民日報(19056行)的重復字符串抽取工作。
2 結構示意圖
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.
- * 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;
- }
- }
- }