数据结构《16》----自动补齐实现《一》----Trie 树
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 树的更多相关文章
- CocoaPods 导入第三方库头文件自动补齐
使用了一段时间CocoaPods来管理Objective-c的类库,方便了不少.但是有一个小问题,当我在xcode输入import关键字的时候,没有自动联想补齐代码的功能,需要手工敲全了文件名,难以适 ...
- 为Debian/Ubuntu的apt-get install添加自动补齐/完成功能
Debian/Ubuntu的apt-get太常用了,不过偶尔可能也会碰到不太熟悉,想不起来的包的名称,除了去debian packages去查找,另外的方法就是给Debian/Ubuntu添加自动补齐 ...
- jquery.autocomplete自动补齐和自定义格式
1.简单的下拉自动补齐,可以使用本地或远程数据源 <input name="autoTag" id="autoTag" /> var source ...
- HTML5的数据自动补齐功能
使用datalist元素,HTML5允许使用一组数据来生成自动补齐功能,现在你不需要使用第三方js代码或者类库啦! <input name="frameworks" list ...
- Android Studio-设置switch/case代码块自动补齐
相信很多和我一样的小伙伴刚从Eclipse转到Android Studio的时候,一定被快捷键给搞得头晕了,像Eclipse中代码补齐的快捷键是Alt+/ ,但是在AS中却要自己设置,这还不是问题的关 ...
- CocoaPods导入第三方库头文件自动补齐
使用了一段时间CocoaPods来管理Objective-c的类库,方便了不少.但是有一个小问题,当我在xcode输入import关键字的时候,没有自动联想补齐代码的功能,需要手工敲全了文件名,难以适 ...
- GBin1插件推荐之马可波罗(Marco Polo),jQuery的自动补齐插件 - Autocomplete Plugin
让我们Google一下"jQuery autocomplete plugin"(jquery自动补齐插件).在过去的4年中,我已经Google了很多次这个组合了.然而结果并没有变化 ...
- 关闭浏览器输入框自动补齐 兼容IE,FF,Chrome等主流浏览器
这篇文章主要介绍了关闭浏览器输入框自动补齐 兼容IE,FF,Chrome等主流浏览器,需要的朋友可以参考下.希望对大家有所帮助 Firefox 和 IE 的浏览器各自实现了input历史记录的功能 ...
- Android Studio 中设置代码块自动补齐
AS中很多提示键,并不如Eclipse中做的好,需要我们自己去自定义.这里以switch...case为例,讲解一下如何设置代码自动补全. 1.进入settings --> Editor -- ...
随机推荐
- 一、Docker之旅
刚刚接触到docker的同事可能会一头雾水,docker到底是一个什么东西,先看看官方的定义. Docker是一个开源的引擎,可以轻松的为任何应用创建一个轻量级的.可移植的.自给自足的容器.开发者在笔 ...
- YTU 3023: 树的遍历
原文链接:https://www.dreamwings.cn/ytu3023/2617.html 3023: 树的遍历 时间限制: 1 Sec 内存限制: 128 MB 提交: 3 解决: 2 题 ...
- linux下解压war格式的包
linux解压 .war 包 war格式的包可以解决web应用程序部署时候不用按照目录层次结构部署,而是将war包当作部署单元来使用. 下面就讲下怎么去解压 .war 格式的压缩包: 1.安装jdk, ...
- [问题2014S13] 解答
[问题2014S13] 解答 (1) 先证必要性:若 \(A=LU\) 是 非异阵 \(A\) 的 \(LU\) 分解,则 \(L\) 是主对角元全部等于 1 的下三角阵,\(U\) 是主对角元全部 ...
- Android 性能分析工具dumpsys的使用(自己增加一部分在后面)
Android提供的dumpsys工具可以用于查看感兴趣的系统服务信息与状态,手机连接电脑后可以直接命令行执行adb shell dumpsys 查看所有支持的Service但是这样输出的太多,可以通 ...
- 备用帖子1Shell(Shell R语言)
shell========================== echo 1 > /proc/sys/vm/drop_caches 清理内存 free -m du -h --max-depth= ...
- IE6兼容性问题及IE6常见bug详细汇总
转载地址:http://www.jb51.net/css/76894.html 1.IE6怪异解析之padding与border算入宽高 原因:未加文档声明造成非盒模型解析 解决方法:加入文档声明&l ...
- 《BI那点儿事》数据流转换——模糊分组转换
在模糊查找中我们提到脏数据是怎样进入到表中的事情,主要还是由于一些“Lazy-add”造成的.这种情况我们的肉眼很容易被欺骗,看上去是同一个单词,其实就差那么一个字母,变成了两个不同的单词.一个简单的 ...
- linux详细redis安装和php中redis扩展
第一部分:安装redis 希望将redis安装到此目录 1 /usr/local/redis 希望将安装包下载到此目录 1 /usr/local/src 那么安装过程指令如下: 1 2 3 4 5 6 ...
- Linux 系统使用之 VMware Tools安装
Red Hat Enterprise Linux 4系统中安装VMware Tools 1. 必须以ROOT身份进入Linux 2. 进入linux系统,然后按下 CTRL+ALT组合键,进入主操作系 ...