1. 简述

Trie 树是一种高效的字符串查找的数据结构。可用于搜索引擎中词频统计,自动补齐等。

在一个Trie 树中插入、查找某个单词的时间复杂度是 O(len), len是单词的长度。

如果采用平衡二叉树来存储的话,时间复杂度是 O(lgN), N为树中单词的总数。

此外,Trie 树还特别擅长 前缀搜索,比方说现在输入法中的自动补齐,输入某个单词的前缀,abs,

立刻弹出 abstract 等单词。

Trie 树优良的查找性能是建立在 牺牲空间复杂度的基础之上的。

本文将给出一个 Trie树的简单实例,并用这个Trie建立了一个单词数目是 7000+的英语词典。

从而分析 Trie 树所占的空间。

2. 定义

一棵典型的 Trie 树,如下图所示:

每一个节点包含一个长度是 26 的指针数组。这 26 个指针分别代表英文 26 个字母。

同时,每个节点拥有一个红色标记,表示 root 到当前的路径是否是一个单词。

例: 下图中最左边的一个路径表示单词 abc 和 abcd.

3.  性能

本人做了一个小测试,当建立一个 7000+ 的词典时,Trie 树共分配了 22383 个节点,每个节点占了 27 * 4 BYTE,

所以共消耗了大约 22383 * 27 * 4 BYTE = 2.4 M

而这 7000 个单词平均长度假设是 8 个字母,那么总共占 7000 * 8 BYTE= 5.6 KB

两者相差 42 倍!!!

从上述小测试可以看到,Trie 树需要占用大量的空间,特别是如果考虑大小写,或者建立汉字的 Trie树时,每个节点所需要的指针数目将更大。

其实,大伙一眼就能发现,Trie 树中,每个节点包含了大量的空指针,因而造成了大量的空间消耗。

可以采用 三叉树(Ternary Search Tree), 改进 Trie 树。将在下一篇文章中讨论。

4. 源码

// Last Update:2014-04-16 23:24:47
/**
* @file trie.h
* @brief Trie
* @author shoulinjun@126.com
* @version 0.1.00
* @date 2014-04-16
*/ #ifndef TRIE_H
#define TRIE_H #include <iostream>
#include <fstream>
#include <string>
#include <cstring>
using std::string;
using std::cout;
using std::endl; const int branchNum = 26; struct TrieNode
{
TrieNode(): isStr(false)
{
memset(next, 0, sizeof(next));
}
bool isStr;
TrieNode* next[branchNum];
}; string ToLower(const string &s)
{
string str;
string::const_iterator it = s.begin();
while(it != s.end())
{
str += (char)tolower(*it);
++ it;
}
return str;
} /**
* a simple data stucture
* usefull for AutoComplete
*/
class Trie
{
public:
Trie(): root(new TrieNode()) {}
~Trie() {
cout << "# of nodes allocated: " << count << endl;
destroy(root); } void Insert(const string &str);
bool Search(const string &str) const;
void AutoComplete(const string &str);
void Input(const string &file); private:
TrieNode* find(const string &str) const;
void dfs(TrieNode *root, string &path);
void destroy(TrieNode * &root); TrieNode *root;
static size_t count;
}; size_t Trie::count = 0; void Trie::destroy(TrieNode * &root)
{
for(int i=0; i<branchNum; ++i)
{
if(root->next[i])
destroy(root->next[i]);
}
delete root;
root = NULL;
} void Trie::Insert(const string &s)
{
if(s.empty()) return; /* support lower cases now */
string str = ToLower(s);
string::const_iterator it = str.begin();
TrieNode *location(root); // bypassing existing nodes
while(it != str.end() && location->next[*it - 'a'] != NULL)
{
location = location->next[*it - 'a'];
++ it;
} // Insert
while(it != str.end() && location->next[*it - 'a'] == NULL)
{
location->next[*it - 'a'] = new TrieNode();
++ count;
location = location->next[*it - 'a'];
++ it;
}
location->isStr = true;
} void Trie::Input(const string &str)
{
std::ifstream ifile(str.c_str()); string word; while(ifile >> word)
{
Insert(word);
} ifile.close();
} bool Trie::Search(const string &s) const
{
TrieNode *location = root; string str = ToLower(s);
location = find(str);
return (location) && location->isStr;
} TrieNode* Trie::find(const string &str) const
{
TrieNode *location = root;
string::const_iterator it = str.begin();
while(it != str.end() && location->next[*it - 'a'] != NULL)
{
location = location->next[*it - 'a'];
++ it;
}
return (it == str.end()) ? location : NULL;
} void Trie::dfs(TrieNode *root, string &path)
{
if(root == NULL) return; if(root->isStr)
cout << path << endl;
for(char x='a'; x<='z'; ++x)
{
if(root->next[x-'a'] != NULL)
{
path += x;
dfs(root->next[x-'a'], path);
path.resize(path.size()-1);
}
}
} void Trie::AutoComplete(const string &str)
{
TrieNode *location(root);
string path; location = find(str);
path = str;
dfs(location, path);
} #endif /*TRIE_H*/

数据结构《16》----自动补齐实现《一》----Trie 树的更多相关文章

  1. CocoaPods 导入第三方库头文件自动补齐

    使用了一段时间CocoaPods来管理Objective-c的类库,方便了不少.但是有一个小问题,当我在xcode输入import关键字的时候,没有自动联想补齐代码的功能,需要手工敲全了文件名,难以适 ...

  2. 为Debian/Ubuntu的apt-get install添加自动补齐/完成功能

    Debian/Ubuntu的apt-get太常用了,不过偶尔可能也会碰到不太熟悉,想不起来的包的名称,除了去debian packages去查找,另外的方法就是给Debian/Ubuntu添加自动补齐 ...

  3. jquery.autocomplete自动补齐和自定义格式

    1.简单的下拉自动补齐,可以使用本地或远程数据源 <input name="autoTag" id="autoTag" /> var source ...

  4. HTML5的数据自动补齐功能

    使用datalist元素,HTML5允许使用一组数据来生成自动补齐功能,现在你不需要使用第三方js代码或者类库啦! <input name="frameworks" list ...

  5. Android Studio-设置switch/case代码块自动补齐

    相信很多和我一样的小伙伴刚从Eclipse转到Android Studio的时候,一定被快捷键给搞得头晕了,像Eclipse中代码补齐的快捷键是Alt+/ ,但是在AS中却要自己设置,这还不是问题的关 ...

  6. CocoaPods导入第三方库头文件自动补齐

    使用了一段时间CocoaPods来管理Objective-c的类库,方便了不少.但是有一个小问题,当我在xcode输入import关键字的时候,没有自动联想补齐代码的功能,需要手工敲全了文件名,难以适 ...

  7. GBin1插件推荐之马可波罗(Marco Polo),jQuery的自动补齐插件 - Autocomplete Plugin

    让我们Google一下"jQuery autocomplete plugin"(jquery自动补齐插件).在过去的4年中,我已经Google了很多次这个组合了.然而结果并没有变化 ...

  8. 关闭浏览器输入框自动补齐 兼容IE,FF,Chrome等主流浏览器

    这篇文章主要介绍了关闭浏览器输入框自动补齐 兼容IE,FF,Chrome等主流浏览器,需要的朋友可以参考下.希望对大家有所帮助   Firefox 和 IE 的浏览器各自实现了input历史记录的功能 ...

  9. Android Studio 中设置代码块自动补齐

    AS中很多提示键,并不如Eclipse中做的好,需要我们自己去自定义.这里以switch...case为例,讲解一下如何设置代码自动补全. 1.进入settings -->  Editor -- ...

随机推荐

  1. placeholder兼容ie8

    <script type="text/javascript">     if( !('placeholder' in document.createElement('i ...

  2. hadoop运维经验

    0.优化:http://dongxicheng.org/mapreduce/hadoop-optimization-0/ http://dongxicheng.org/mapreduce/hadoop ...

  3. [问题2014S13] 复旦高等代数II(13级)每周一题(第十三教学周)

    [问题2014S13]  (1)  设 \(A\) 是数域 \(\mathbb{K}\) 上的 \(n\) 阶非异阵, 若存在主对角元全为 \(1\) 的下三角阵 \(L\in M_n(\mathbb ...

  4. sourceforge免费空间申请及使用笔记

    sourceforge免费空间申请及使用笔记 sourceforge免费空间安装WordPress博客程序 WordPress博客程序安装文件的上传需要使用工具WinSCP. 你需要在FTP地址填写的 ...

  5. iOS FMDB的使用

    简介: SQLite (http://www.sqlite.org/docs.html) 是一个轻量级的关系数据库.iOS SDK 很早就支持了 SQLite,在使用时,只需要加入 libsqlite ...

  6. CentOS 7系统挂载NTFS分区的移动硬盘(转载及体验 CentOS6.5系统挂载NTFS分区的移动硬盘)

    作为IT的工作者,避免不了使用Linux系统,我比较喜欢CentOS,为了锻炼自己对CentOS的熟练操作,就把自己的笔记本装了CentOS,强制自己使用,使自己在平时的工作中逐渐掌握Linux的学习 ...

  7. hiho_1141

    题目 按顺序给出N个数字,求出所有的逆序对个数(逆序对指数字 Ai > Aj且 i < j) 题目链接:hiho_1141     数据规模为 100000,必须使用O(nlogn)的算法 ...

  8. 打造高大上的Canvas粒子(一)

    HTML5 Canvas <canvas>标签定义图形,比如图表和其他图像,必须用脚本(javascript)绘制图形. 举例:绘制矩形 <script> var c = do ...

  9. ring0

    Intel的x86处理器是通过Ring级别来进行访问控制的,级别共分4层,RING0,RING1,RING2,RING3.Windows只使用其中的两个级别RING0和RING3. RING0层拥有最 ...

  10. JDK配置

    一.下载和安装 二.配置环境变量 1.计算机→属性→高级系统设置→高级→环境变量 2.系统变量→新建JAVA_HOME,值为jdk的安装目录(如C:\Java\jdk1.7.0) 3.系统变量→修改P ...