概述:

Trie是个简单但实用的数据结构,是一种树形结构,是一种哈希树的变种,相邻节点间的边代表一个字符,这样树的每条分支代表一则子串,而树的叶节点则代表完整的字符串。和普通树不同的地方是,相同的字符串前缀共享同一条分支。

例如:pool,prize,preview,prepare,produce,progress这些关键词的Tire树

典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。

它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。

基本性质:

  • 根节点不包含字符,除根节点外每一个节点都只包含一个字符;
  • 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串;
  • 每个节点的所有子节点包含的字符都不相同。

应用场景:

字典数查找效率很高,时间复杂度是O(m),m是要查找的单词中包含的字母的个数,但是会浪费大量存放空指针的存储空间,属于以空间换时间的算法。

1、串快速检索

给出N个单词组成的熟词表,以及一篇全用小写英文书写的文章,请你按最早出现的顺序写出所有不在熟词表中的生词。

2、单词自动完成

编辑代码时,输入字符,自动提示可能的关键字、变量或函数等信息。

3、最长公共前缀

对所有串建立字典树,对于两个串的最长公共前缀的长度即他们所在的结点的公共祖先个数,于是,问题就转化为最近公共祖先问题。

4、串排序方面的应用

给定N个互不相同的仅由一个单词构成的英文名,让你将他们按字典序从小到大输出用字典树进行排序,采用数组的方式创建字典树,这棵树的每个结点的所有儿子很显然地按照其字母大小排序。对这棵树进行先序遍历即可。

程序代码:

#include <gtest/gtest.h>
#include <list>
using namespace std; class TrieTree
{
public: const static int MAX_CHILD_KEY_COUNT = 30;
const static char STRING_END_TAG = '\xFF';
struct TrieNode
{
char nodeValue;
int nodeFreq;
list<TrieNode*> childNodes[MAX_CHILD_KEY_COUNT]; //为了避免这里数组太大,采用数组+链表方式 TrieNode()
{
nodeValue = 0;
nodeFreq = 0;
}
}; TrieTree();
~TrieTree(); public:
void Insert(const string& strVal);
void Delete(const string& strVal);
int Search(const string& strVal);
int CommonPrefix(const string& strVal); private:
void Clean(TrieNode* rootNode);
bool DeleteNode(TrieNode* rootNode, const string& strVal, int nOffset); TrieNode m_RootNode;
}; TrieTree::TrieTree()
{
}; TrieTree::~TrieTree()
{
Clean(&m_RootNode);
}; void TrieTree::Insert(const string& strVal)
{
if (strVal.empty())
{
return;
} // 在字符串末尾添加一个特殊字符,以区分是前缀还是完整字符串
string strValue(strVal);
strValue += STRING_END_TAG; TrieNode* pCurrentNode = &m_RootNode;
unsigned int nIndex = 0;
unsigned int nLength = strValue.length(); do
{
bool bExistVal = false;
char cValue = strValue[nIndex];
list<TrieNode*>& refListNode = pCurrentNode->childNodes[(unsigned char)cValue % MAX_CHILD_KEY_COUNT];
if (refListNode.size())
{
list<TrieNode*>::iterator it = refListNode.begin();
list<TrieNode*>::iterator itEnd = refListNode.end();
for (; it != itEnd; ++it)
{
if (cValue == (*it)->nodeValue)
{
(*it)->nodeFreq++;
bExistVal = true;
pCurrentNode = *it; break;
}
}
} // 当前不存在对应的字符,则新建一个
if (!bExistVal)
{
TrieNode* pNewNode = new TrieNode();
pNewNode->nodeFreq = 1;
pNewNode->nodeValue = cValue; refListNode.push_back(pNewNode);
pCurrentNode = pNewNode;
} ++nIndex;
}
while(nIndex < nLength);
} void TrieTree::Delete(const string& strVal)
{
if (strVal.empty())
{
return;
} string strValue(strVal);
strValue += STRING_END_TAG; DeleteNode(&m_RootNode, strValue, 0);
} int TrieTree::Search(const string& strVal)
{
if (strVal.empty())
{
return 0;
} string strValue(strVal);
strValue += STRING_END_TAG; return CommonPrefix(strValue);
} int TrieTree::CommonPrefix(const string& strVal)
{
if (strVal.empty())
{
return 0;
} TrieNode* pCurrentNode = &m_RootNode;
unsigned int nIndex = 0;
unsigned int nLength = strVal.length();
int nFreq = 0; do
{
bool bExistVal = false;
char cValue = strVal[nIndex];
list<TrieNode*>& refListNode = pCurrentNode->childNodes[(unsigned char)cValue % MAX_CHILD_KEY_COUNT];
if (refListNode.size())
{
list<TrieNode*>::iterator it = refListNode.begin();
list<TrieNode*>::iterator itEnd = refListNode.end();
for (; it != itEnd; ++it)
{
if (cValue == (*it)->nodeValue)
{
nFreq = (*it)->nodeFreq;
bExistVal = true;
pCurrentNode = *it;
break;
}
}
} // 当前不存在对应的字符,则没有找到
if (!bExistVal)
{
nFreq = 0;
break;
} ++nIndex;
}
while(nIndex < nLength); return nFreq;
} void TrieTree::Clean(TrieNode* rootNode)
{
if (!rootNode)
{
return;
} for (int i=0; i<MAX_CHILD_KEY_COUNT; ++i)
{
list<TrieNode*>& refListNode = rootNode->childNodes[i];
if (refListNode.size())
{
list<TrieNode*>::iterator it = refListNode.begin();
list<TrieNode*>::iterator itEnd = refListNode.end();
for (; it != itEnd; ++it)
{
Clean(*it);
delete *it;
} refListNode.clear();
}
}
} bool TrieTree::DeleteNode(TrieNode* rootNode, const string& strVal, int nOffset)
{
if (!rootNode)
{
return false;
} bool bDelChild = false;
char cValue = strVal[nOffset];
list<TrieNode*>& refListNode = rootNode->childNodes[(unsigned char)cValue % MAX_CHILD_KEY_COUNT];
if (refListNode.size())
{
list<TrieNode*>::iterator it = refListNode.begin();
list<TrieNode*>::iterator itEnd = refListNode.end();
for (; it != itEnd; ++it)
{
if ((*it)->nodeValue == cValue)
{
bDelChild = true;
// 字符串没有结束,删除下一个节点
if (++nOffset < (int)strVal.length())
{
bDelChild = DeleteNode(*it, strVal, nOffset);
} // 该节点次数为0,说明已经没有子节点了,移除该节点
if (bDelChild &&
(0 == (--(*it)->nodeFreq)))
{
delete *it;
refListNode.erase(it);
} break;
}
}
} return bDelChild;
} TEST(Structure, tTireTree)
{
// "abc","ab","bd","dda"
TrieTree tree;
tree.Insert("abc");
tree.Insert("ab");
tree.Insert("bd");
tree.Insert("dda"); ASSERT_EQ(tree.Search("ab"), 1);
ASSERT_EQ(tree.CommonPrefix("ab"), 2);
tree.Delete("ab");
ASSERT_EQ(tree.Search("ab"), 0);
ASSERT_EQ(tree.CommonPrefix("ab"), 1);
tree.Delete("abcd");
ASSERT_EQ(tree.Search("ab"), 0);
ASSERT_EQ(tree.Search("d"), 0);
ASSERT_EQ(tree.CommonPrefix("d"), 1);
ASSERT_EQ(tree.Search("fg"), 0);
tree.Delete("fg");
}

参考引用:

百度百科“字典树”

http://www.cnblogs.com/huangxincheng/archive/2012/11/25/2788268.html

  看书、学习、写代码

[数据结构]字典树(Tire树)的更多相关文章

  1. 字典(Tire树)

    4189 字典  时间限制: 1 s  空间限制: 256000 KB  题目等级 : 大师 Master     题目描述 Description 最经,skyzhong得到了一本好厉害的字典,这个 ...

  2. 基于Tire树和最大概率法的中文分词功能的Java实现

    对于分词系统的实现来说,主要应集中在两方面的考虑上:一是对语料库的组织,二是分词策略的制订. 1.   Tire树 Tire树,即字典树,是通过字串的公共前缀来对字串进行统计.排序及存储的一种树形结构 ...

  3. Tire树简介

    又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种. 典型应用:用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计. 它的优点是:利用字符串的公共 ...

  4. Atitit 常见的树形结构 红黑树  二叉树   B树 B+树  Trie树 attilax理解与总结

    Atitit 常见的树形结构 红黑树  二叉树   B树 B+树  Trie树 attilax理解与总结 1.1. 树形结构-- 一对多的关系1 1.2. 树的相关术语: 1 1.3. 常见的树形结构 ...

  5. 海量数据处理之Tire树(字典树)

    参考博文:http://blog.csdn.net/v_july_v/article/details/6897097 第一部分.Trie树 1.1.什么是Trie树 Trie树,即字典树,又称单词查找 ...

  6. Tire树(字典树)

    from:https://www.cnblogs.com/justinh/p/7716421.html Trie,又经常叫前缀树,字典树等等.它有很多变种,如后缀树,Radix Tree/Trie,P ...

  7. 9-11-Trie树/字典树/前缀树-查找-第9章-《数据结构》课本源码-严蔚敏吴伟民版

    课本源码部分 第9章  查找 - Trie树/字典树/前缀树(键树) ——<数据结构>-严蔚敏.吴伟民版        源码使用说明  链接☛☛☛ <数据结构-C语言版>(严蔚 ...

  8. 【Todo】字符串相关的各种算法,以及用到的各种数据结构,包括前缀树后缀树等各种树

    另开一文分析字符串相关的各种算法,以及用到的各种数据结构,包括前缀树后缀树等各种树. 先来一个汇总, 算法: 本文中提到的字符串匹配算法有:KMP, BM, Horspool, Sunday, BF, ...

  9. Tire树总结(模板+例题)

    题目来自<算法竞赛设计指南> Tire树是一种可以快速查找字符串的数据结构 模板 #include<cstdio> #include<algorithm> #inc ...

随机推荐

  1. Linux查找文件夹名

    @(编程) find / -type d -name filename type的类型 -type c File is of type c: b block (buffered) special c ...

  2. sql数据库delete删除后怎么恢复,这是网上找的答案。。希望大神验证指教一下

    使用Log Explorer查看和恢复数据 Log Explorer 4.1.可用于SQL Server2005的日志查看工具 下载地址: http://download.csdn.net/sourc ...

  3. Spring REST实践之Spring Web MVC

    Spring概要 Spring Framework提供了依赖注入模型和面向切面编程,简化了基础型代码的编写工作以及更好的能够与其它框架和技术整合起来.Spring Framework由data acc ...

  4. 计数排序详解以及java实现

    前言 我们知道,通过比较两个数大小来进行排序的算法(比如插入排序,合并排序,以及上文提到的快速排序等)的时间复杂度至少是Θ(nlgn),这是因为比较排序对应的决策树的高度至少是Θ(nlgn),所以排序 ...

  5. JQuery 的bind和unbind函数

    测试:页面代码:<body>     <input type="button" name="aaa" value="点击我" ...

  6. android 检测ListView滚动到的位置

    ListView滚动 1.要用到一个监听事件是:setOnScrollListener(); API解释是: Set the listener that will receive notificati ...

  7. 解析Ceph: Snapshot

    经常有开发者在邮件列表中会问到Ceph Snapshot的实现方式,受限于目前有限的实现文档和复杂的代码结构和代码量,弄清楚Ceph Snapshot并不是一件容易的事.正好最近在重构Ceph存储引擎 ...

  8. css初始化代码方案

    (从已经死了一次又一次终于挂掉的百度空间人工抢救出来的,发表日期 2014-06-24) 为了消除各浏览器对css默认的设置,保持网页在各浏览器中的外观保持一致,初始化css就显得非常必要了!很多时候 ...

  9. $(document).ready()与 window.onload执行时机

    $(document).ready()方法和window.onload方法有相似的功能,但是在执行时机方面是有区别的.window.onload方法是子啊网页中的所有元素(包括元素的所有关联的文件)完 ...

  10. Linq使用Group By经验总结

    1.计数 var q = from p in db.Products group p by p.CategoryID into g select new { g.Key, NumProducts = ...