歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> 一個通用的Trie樹,標准C++實現

一個通用的Trie樹,標准C++實現

日期:2017/3/1 10:25:25   编辑:Linux編程

1 Trie簡介

Trie樹,又稱單詞查找樹鍵樹,是一種樹形結構,是一種哈希樹的變種。典型應用是用於統計和排序大量的字符串(但不僅限於字符串),所以經常被搜索引擎系統用於文本詞頻統計。它的優點是:最大限度地減少無謂的字符串比較,查詢效率比哈希表高。

在本文中,對於輸入的進行序列化,比如輸入“單詞查找樹”,序列化為“單/詞/查/找/樹”,這樣可以進行任何一種自定義的數據插入和查詢。序列化輸入可以根據自己的需要進行相應的改動,這樣可以把Trie樹結構應用到很多其他的語言和領域。

本Trie樹結構的優點在於

1 不限制子節點的數量;

2 自定義的輸入序列化,突破了具體語言、應用的限制,成為一個通用的框架;

3 可以進行最大Tokens序列長度的限制;

4 根據已定阈值輸出重復的字符串;

5 提供單個字符串頻度查找功能;

6 速度快,在兩分鐘內完成1998年1月份人民日報(19056行)的重復字符串抽取工作。

2 結構示意圖

3 實現代碼

Trie.h

  1. /********************************************************************
  2. * Copyright (C) 2012 Li Yachao
  3. * Contact: [email protected] or [email protected]
  4. *
  5. * Permission to use, copy, modify, and distribute this software for
  6. * any non-commercial purpose is hereby granted without fee, provided
  7. * that the above copyright notice appear in all copies and that both
  8. * that copyright notice.
  9. * It is provided "as is" without express or implied warranty.
  10. * http://www.linuxidc.com
  11. * Version: 0.1
  12. * Last update: 2012-4-2
  13. *********************************************************************/
  14. /*********************************************************************
  15. *********************************************************************/
  16. #ifndef TRIE_H
  17. #define TRIE_H
  18. #include <iostream>
  19. #include <fstream>
  20. #include <string>
  21. #include <vector>
  22. #include <stdio.h>
  23. namespace MyUtility
  24. {
  25. /*用於存儲原子數據的數據結構*/
  26. typedef struct TrieNode
  27. {
  28. char* token;/*Trie節點的token值*/
  29. bool terminal;/*當前節點是否是終結點*/
  30. struct TrieNode* sons;/*子節點*/
  31. struct TrieNode* next;/*兄弟節點*/
  32. }TrieNode;
  33. /*輸出結果的數據結構*/
  34. typedef struct StrFreq
  35. {
  36. std::string Str;/*字符串*/
  37. int Freq;/*頻率*/
  38. }StrFreq;
  39. class Trie
  40. {
  41. public:
  42. Trie()
  43. {
  44. CreateRoot();
  45. travel_path.clear();
  46. result.clear();
  47. threshhold = 3;
  48. maxLength = 9 ;
  49. fout.open("result.txt");
  50. }
  51. ~Trie()
  52. {
  53. Destroy();
  54. }
  55. /*設置輸出重復字符串頻率的阈值*/
  56. void SetThreshhold(int ts)
  57. {
  58. if(ts<=1)
  59. {
  60. return ;
  61. }
  62. threshhold = ts;
  63. }
  64. /*設置最長的字符串匹配長度的阈值*/
  65. void SetMaxLength(int max_leng)
  66. {
  67. if(max_leng <= 1)
  68. {
  69. return ;
  70. }
  71. maxLength = max_leng;
  72. }
  73. /*輸出結果*/
  74. void Print(std::vector<StrFreq>& result);
  75. void Print();
  76. bool AddString(const std::string& str);
  77. /*取得一個字符串的重復頻率*/
  78. int StrFrequency(const char* str);
  79. /*清空Trie樹*/
  80. bool Clear();
  81. private:
  82. std::ofstream fout;
  83. TrieNode * Root;/*Trie樹根節點*/
  84. std::vector<std::string>travel_path;/*遍歷是的訪問路徑*/
  85. std::vector<StrFreq>result;/*重復字符串的輸出結果*/
  86. int sub_sons;/*一個節點的子節點數量*/
  87. int threshhold;/*重復字符串輸出阈值,默認為2*/
  88. int maxLength;/*最長的Tokens序列長度,默認為9*/
  89. void Tokenize(const std::string& str,std::vector<std::string>&vec_tokens);
  90. TrieNode * InsertNode(TrieNode* node,const char *token,bool end = false);
  91. /*查找一個節點是否有子節點值為token的節點,返回子節點的指針*/
  92. TrieNode * FindNode(TrieNode* p_node,const char *token);
  93. /*初始化一個新的Trie節點*/
  94. inline TrieNode* NewNode()
  95. {
  96. TrieNode * newNode = new TrieNode();
  97. newNode->sons = NULL;
  98. newNode->next = NULL;
  99. newNode->token = NULL;
  100. newNode->terminal = false;
  101. return newNode;
  102. }
  103. /*初始化一個新的Trie樹根節點*/
  104. void CreateRoot()
  105. {
  106. if( NULL != Root)
  107. {
  108. delete Root;
  109. Root = NULL;
  110. }
  111. Root = NewNode();
  112. char * root_tag ="Root";
  113. Root->token = new char[sizeof(char)*strlen(root_tag)];
  114. strcpy(Root->token,root_tag);
  115. }
  116. /*銷毀Trie樹*/
  117. void Destroy();
  118. /*銷毀Trie子樹*/
  119. void Destroy(TrieNode * node);
  120. /*遍歷樹結構*/
  121. void Travel(TrieNode* node);
  122. /*取得一個節點的子節點數*/
  123. void TrieNodeSons(const TrieNode* node);
  124. void TrieNodeSons(const TrieNode* node,const TrieNode* root);
  125. };
  126. }
  127. #endif
Trie.cpp
  1. #include "Trie.h"
  2. namespace MyUtility
  3. {
  4. /*
  5. *************************************************
  6. 功能 : 中文文本預處理,序列化輸入
  7. 參數 :
  8. 返回值 :
  9. -------------------------------------------------
  10. 備注 :
  11. -------------------------------------------------
  12. 作者 :Li Yachao
  13. 時間 :2012-4-3
  14. *************************************************
  15. */
  16. void Trie::Tokenize(const std::string &str, std::vector<std::string> &vec_tokens)
  17. {
  18. vec_tokens.clear();
  19. std::string tmp ="";
  20. if(str.empty())
  21. {
  22. return ;
  23. }
  24. for(int i=0;i<str.size();i++)
  25. {
  26. unsigned char c = str[i];
  27. if(c < 128)
  28. {
  29. tmp = str.substr(i,1);
  30. vec_tokens.push_back(tmp);
  31. }
  32. else
  33. {
  34. tmp = str.substr(i,2);
  35. vec_tokens.push_back(tmp);
  36. i++;
  37. }
  38. }
  39. }
  40. /*
  41. *************************************************
  42. 功能 : 銷毀Trie樹
  43. 參數 :
  44. 返回值 :
  45. -------------------------------------------------
  46. 備注 :
  47. -------------------------------------------------
  48. 作者 :Li Yachao
  49. 時間 :2012-4-3
  50. *************************************************
  51. */
  52. void Trie::Destroy()
  53. {
  54. Destroy(Root);
  55. }
  56. void Trie::Destroy(TrieNode * node)
  57. {
  58. if(NULL != node)
  59. {
  60. Destroy(node->sons);
  61. Destroy(node->next);
  62. delete node;
  63. node = NULL;
  64. }
  65. else
  66. {
  67. return ;
  68. }
  69. }
  70. /*
  71. *************************************************
  72. 功能 : 清空Trie樹
  73. 參數 :
  74. 返回值 :
  75. -------------------------------------------------
  76. 備注 :
  77. -------------------------------------------------
  78. 作者 :Li Yachao
  79. 時間 :2012-4-3
  80. *************************************************
  81. */
  82. bool Trie::Clear()
  83. {
  84. Destroy();
  85. CreateRoot();
  86. travel_path.clear();
  87. result.clear();
  88. return true;
  89. }
  90. /*
  91. *************************************************
  92. 功能 : 取得一個Trie數節點的子節點數,即一個Token序列的重復次數。
  93. 參數 :
  94. 返回值 :
  95. -------------------------------------------------
  96. 備注 :
  97. -------------------------------------------------
  98. 作者 :Li Yachao
  99. 時間 :2012-4-3
  100. *************************************************
  101. */
  102. void Trie::TrieNodeSons(const TrieNode * node,const TrieNode* root)
  103. {
  104. if(NULL != node)
  105. {
  106. TrieNodeSons(node->sons,root);
  107. if(node->terminal)
  108. {
  109. sub_sons++;/*以Token序列是否是序列結尾為標志*/
  110. }
  111. if(node != root)
  112. {/*根節點不能遍歷其兄弟節點*/
  113. TrieNodeSons(node->next,root);
  114. }
  115. }
  116. else
  117. {
  118. return ;
  119. }
  120. }
  121. /*void Trie::TrieNodeSons(const TrieNode * node)
  122. {
  123. if(NULL != node)
  124. {
  125. TrieNodeSons(node->sons);
  126. if(node->terminal)
  127. {
  128. sub_sons++;
  129. }
  130. if(node != Root)
  131. {
  132. TrieNodeSons(node->next);
  133. }
  134. }
  135. else
  136. {
  137. return ;
  138. }
  139. }*/
  140. /*
  141. *************************************************
  142. 功能 : 遍歷Trie數所有的節點,根據設定的threashold輸出Token序列
  143. 參數 :
  144. 返回值 :
  145. -------------------------------------------------
  146. 備注 :
  147. -------------------------------------------------
  148. 作者 :Li Yachao
  149. 時間 :2012-4-3
  150. *************************************************
  151. */
  152. void Trie::Travel(TrieNode* node)
  153. {
  154. if(NULL != node)
  155. {
  156. if(node != Root)
  157. travel_path.push_back(node->token);
  158. Travel(node->sons);
  159. /********************************************************/
  160. sub_sons =0;
  161. //TrieNodeSons(node);
  162. TrieNodeSons(node,node);
  163. int sum = sub_sons;
  164. //sub_sons = 0;
  165. //TrieNodeSons(node->sons,node->sons);
  166. if((sub_sons >= threshhold))
  167. //if((sum != sub_sons) && (sum >= threshhold))
  168. {
  169. std::string buf="";
  170. for(int i=0;i<travel_path.size();i++)
  171. {
  172. buf += travel_path[i];
  173. }
  174. if(!buf.empty())
  175. {
  176. //fout<<buf<<"\t"<<sum<<std::endl;
  177. fout<<buf<<"\t"<<sub_sons<<std::endl;
  178. }
  179. }
  180. if(travel_path.size() > 0)
  181. {
  182. travel_path.pop_back();
  183. }
  184. /********************************************************/
  185. Travel(node->next);
  186. /********************************************************/
  187. /********************************************************/
  188. }
  189. else
  190. {
  191. return ;
  192. }
  193. }
  194. void Trie::Print()
  195. {
  196. travel_path.clear();
  197. result.clear();
  198. Travel(Root);
  199. std::cout<<"String\tFrequency"<<std::endl;
  200. for(int i=0;i<result.size();i++)
  201. {
  202. std::cout<<result[i].Str <<"\t"<<result[i].Freq <<std::endl;
  203. }
  204. result.clear();
  205. }
  206. void Trie::Print(std::vector<StrFreq>& re_result)
  207. {
  208. travel_path.clear();
  209. result.clear();
  210. Travel(Root);
  211. //re_result = result;
  212. //result.clear();
  213. }
  214. /*
  215. *************************************************
  216. 功能 : 輸入Trie樹字符串
  217. 參數 :
  218. 返回值 :
  219. -------------------------------------------------
  220. 備注 :
  221. -------------------------------------------------
  222. 作者 :Li Yachao
  223. 時間 :2012-4-3
  224. *************************************************
  225. */
  226. bool Trie::AddString(const std::string &str)
  227. {
  228. std::vector<std::string>val;
  229. std::vector<std::string>val_tmp;
  230. Tokenize(str,val);
  231. int step = maxLength;
  232. for(int i=0;i<val.size();i++)
  233. {
  234. val_tmp.clear();
  235. while((i+step) > val.size())
  236. {
  237. step --;
  238. }
  239. for(int j=i;j< (i+step);j++)
  240. {
  241. val_tmp.push_back(val[j]);
  242. }
  243. TrieNode* cur = Root;
  244. for(int j=0;j<val_tmp.size();j++)
  245. {
  246. if(j == val_tmp.size() - 1)
  247. {
  248. InsertNode(cur,val_tmp[j].c_str(),true);
  249. }
  250. else
  251. {
  252. cur = InsertNode(cur,val_tmp[j].c_str());
  253. }
  254. }
  255. }
  256. return true;
  257. }
  258. /*
  259. *************************************************
  260. 功能 : 插入Trie樹節點
  261. 參數 :
  262. 返回值 : 插入節點的指針
  263. -------------------------------------------------
  264. 備注 :
  265. -------------------------------------------------
  266. 作者 :Li Yachao
  267. 時間 :2012-4-3
  268. *************************************************
  269. */
  270. TrieNode * Trie::InsertNode(TrieNode* node,const char *token,bool end)
  271. {
  272. if(NULL == node)
  273. {
  274. return NULL;
  275. }
  276. if(NULL == node->sons)
  277. {
  278. node->sons = NewNode();
  279. node->sons->token = new char[sizeof(char)*strlen(token)];
  280. strcpy(node->sons->token,token);
  281. if(end)
  282. {
  283. node->sons->terminal = true;
  284. }
  285. return node->sons;
  286. }
  287. else
  288. {
  289. TrieNode* cur = node->sons;
  290. while(NULL != cur->next)
  291. {
  292. if(strcmp(cur->token,token) == 0)
  293. {
  294. if(end)
  295. {
  296. cur->terminal = true;
  297. }
  298. return cur ;
  299. }
  300. if( NULL != cur->next)
  301. {
  302. cur = cur->next ;
  303. }
  304. else
  305. {
  306. break;
  307. }
  308. }
  309. if(strcmp(cur->token ,token) == 0)
  310. {
  311. if(end)
  312. {
  313. cur->terminal = true;
  314. }
  315. return cur;
  316. }
  317. TrieNode* n = NewNode();
  318. n->token = new char[sizeof(char)*strlen(token)];
  319. strcpy(n->token ,token);
  320. if(end)
  321. {
  322. n->terminal = true;
  323. }
  324. cur->next = n;
  325. return n;
  326. }
  327. }
  328. /*
  329. *************************************************
  330. 功能 : 查找一個字符串的重復次數
  331. 參數 :
  332. 返回值 :
  333. -------------------------------------------------
  334. 備注 :
  335. -------------------------------------------------
  336. 作者 :Li Yachao
  337. 時間 :2012-4-3
  338. *************************************************
  339. */
  340. int Trie::StrFrequency(const char* str)
  341. {
  342. std::vector<std::string>tokens;
  343. Tokenize(str,tokens);
  344. TrieNode * cur = Root;
  345. for(int i=0;i<tokens.size();i++)
  346. {
  347. cur = FindNode(cur,tokens[i].c_str());
  348. }
  349. if(NULL == cur)
  350. {
  351. return 0;
  352. }
  353. else
  354. {
  355. sub_sons =0;
  356. TrieNodeSons(cur, cur);
  357. return sub_sons;
  358. }
  359. }
  360. /*
  361. *************************************************
  362. 功能 : 查找一個節點的指針
  363. 參數 :
  364. 返回值 :
  365. -------------------------------------------------
  366. 備注 :
  367. -------------------------------------------------
  368. 作者 :Li Yachao
  369. 時間 :2012-4-3
  370. *************************************************
  371. */
  372. TrieNode * Trie::FindNode(TrieNode *p_node, const char *token)
  373. {
  374. if((NULL != p_node) && (NULL != p_node->sons))
  375. {
  376. TrieNode *cur = p_node->sons;
  377. while(NULL != cur)
  378. {
  379. if(strcmp(cur->token,token) == 0)
  380. {
  381. return cur;
  382. }
  383. cur = cur->next;
  384. }
  385. return NULL;
  386. }
  387. else
  388. {
  389. return NULL;
  390. }
  391. }
  392. }
Copyright © Linux教程網 All Rights Reserved