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. 【Spring】搭建最简单的Spring MVC项目

    每次需要Spring MVC的web项目测试一些东西时,都苦于手头上没有最简单的Spring MVC的web项目,现写一个. > 版本说明 首先要引入一些包,Spring的IOC.MVC包就不用 ...

  2. [问题2015S09] 复旦高等代数 II(14级)每周一题(第十教学周)

    [问题2015S09]  设 \(A,B\) 是 \(n\) 阶复矩阵, 满足 \(\mathrm{rank}(AB-BA)\leq 1\), 证明: \(A,B\) 可同时上三角化. 问题解答请在以 ...

  3. 大分享-hibernate,springmvc,easyui简要介绍

    近期公司一直在做项目,主要用到了springMVC,eseayui,hibernate几大框架.近一个月的时间,个人就目前自我知识给予分享. 很多公司使用mybatis产品,综合所述其最大优点是全SQ ...

  4. 文件上传和下载(可批量上传)——Spring(二)

    针对SpringMVC的文件上传和下载.下载用之前“文件上传和下载——基础(一)”的依然可以,但是上传功能要修改,这是因为springMVC 都为我们封装好成自己的文件对象了,转换的过程就在我们所配置 ...

  5. C#的多态性

    参考网址:http://www.cnblogs.com/zhangkai2237/archive/2012/12/20/2826734.html 多态的定义:同一操作作用于不同的对象,可以有不同的解释 ...

  6. jquery总结05-常用事件03-键盘事件

    键盘事件 .keydown() 键盘按下触发,返回的的是键盘按键代码 .keyup() 键盘松手触发,返回的是键盘按键代码 .keypress() 键盘按下触发,返回的是敲击的字符的ASCII码

  7. Java设置环境变量的含义(JAVA_HOME,PATH,CLASSPATH)

    开发Java程序之前,需要在计算机行安装并配置Java开发环境.一种是直接安装Myeclipse,利用其自带的JDK编译运行:另一种是在我们的Windows或者Linux平台下安装JDK,配置环境变量 ...

  8. seek指针大文件上传

    package mainimport (    // "bufio"    "fmt"    "github.com/axgle/mahonia&qu ...

  9. ubuntu 安装eclipse,adt,android sdk,离线

    1.安装jdk 环境变量 $ sudo gedit ~/.bashrc export JAVA_HOME=/usr/local/jdk1.8.0_65export JRE_HOME=${JAVA_HO ...

  10. stm32cube--串口

    1.printf函数重定向 ①以stm32f103rct6的usart1为例,打开cube,配置好RCC和USART1,生成mdk程序. ②打开工程,在main.c中 /* USER CODE BEG ...