摘自hackbuteer1

Trie树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。

Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。

Trie 的强大之处就在于它的时间复杂度。它的插入和查询时间复杂度都为 O(k) ,其中 k 为 key 的长度,与 Trie 中保存了多少个元素无关。Hash 表号称是 O(1) 的,但在计算 hash 的时候就肯定会是 O(k) ,而且还有碰撞之类的问题;Trie 的缺点是空间消耗很高。

基本性质:
(1)根节点不包含字符,除根节点意外每个节点只包含一个字符。
(2)从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。
(3)每个节点的所有子节点包含的字符串不相同。

特性:
1)根节点不包含字符,除根节点外每一个节点都只包含一个字符。
2)从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
3)每个节点的所有子节点包含的字符都不相同。
4)如果字符的种数为n,则每个结点的出度为n,这也是空间换时间的体现,浪费了很多的空间。
5)插入查找的复杂度为O(n),n为字符串长度。

基本思想(以字母树为例):
1、插入过程
    
对于一个单词,从根开始,沿着单词的各个字母所对应的树中的节点分支向下走,直到单词遍历完,将最后的节点做标记,表示该单词已插入Trie树。
2、查询过程
     从根开始按照单词的字母顺序向下遍历trie树,一旦发现某个节点标记不存在或者单词遍历完成而最后的节点未做标记,则表示该单词不存在,若最后的节点有标记,表示该单词存在。

复杂度:

  建立Trie的复杂度为O(n*len),而建立+查询在trie中是可以同时执行的,建立的过程也就可以成为查询的过程。所以总的复杂度为O(n*len),实际查询的复杂度只是O(len)。

操作:

在Trie树中主要有3个操作,插入、查找和删除。一般情况下Trie树中很少存在删除单独某个结点的情况,因此只考虑删除整棵树。
1、插入

  假设存在字符串str,Trie树的根结点为root。i=0,p=root。
  1)取str[i],判断p->next[str[i]-97]是否为空,若为空,则建立结点temp,并将p->next[str[i]-97]指向temp,然后p指向temp;
  若不为空,则p=p->next[str[i]-97];
  2)i++,继续取str[i],循环1)中的操作,直到遇到结束符'\0',此时将当前结点p中的 exist置为true。

2、查找

  假设要查找的字符串为str,Trie树的根结点为root,i=0,p=root
  1)取str[i],判断判断p->next[str[i]-97]是否为空,若为空,则返回false;若不为空,则p=p->next[str[i]-97],继续取字符。
  2)重复1)中的操作直到遇到结束符'\0',若当前结点p不为空并且 exist 为true,则返回true,否则返回false。

3、删除

  删除可以以递归的形式进行删除。

模板

(静态数组)

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstdlib>
  4. #include<cstring>
  5. using namespace std;
  6. const int MAX_N = ;
  7. typedef struct TrieNode
  8. {
  9. bool is_leaf; //标记到字典树从根到当前结点所构成的字符串是否为一个(颜色)单词
  10. int id; //当前字符串的编号
  11. struct TrieNode* next[];
  12. }TrieNode;
  13. TrieNode Node;
  14. TrieNode Root[MAX_N];
  15. int node_cnt;
  16. int idn;
  17.  
  18. int Insert(char *word)
  19. {
  20. TrieNode *p = &Node;
  21. while(*word)
  22. {
  23. int ch = *word - 'a';
  24. if(p->next[ch] == NULL)
  25. {
  26. Root[node_cnt].is_leaf = false;
  27. Root[node_cnt].id = ;
  28. p->next[ch] = &Root[node_cnt++];
  29. }
  30. p = p->next[ch];
  31. word++;
  32. }
  33. if(p->is_leaf)
  34. return p->id;
  35. p->is_leaf = true;
  36. p->id = ++idn;
  37. return p->id;
  38. }
  39. bool Search(char *word)
  40. {
  41. TrieNode *p = &Node;
  42. while(*word && p)
  43. {
  44. p = p->next[*word-'a'];
  45. word++;
  46. }
  47. return(p != NULL && p->is_leaf);
  48. }

(动态指针)

  1. #include <iostream>
  2. #include <cstring>
  3. #include <cstdlib>
  4. #include <cstdio>
  5. using namespace std;
  6. const int branchNum = ; //声明常量
  7. int i;
  8.  
  9. struct Trie_node
  10. {
  11. bool isStr; //记录此处是否构成一个串。
  12. Trie_node *next[branchNum];//指向各个子树的指针,下标0-25代表26字符
  13. Trie_node()
  14. {
  15. isStr = false;
  16. memset(next,NULL,sizeof(next));
  17. }
  18. };
  19.  
  20. class Trie
  21. {
  22. public:
  23. Trie();
  24. void insert(const char* word);
  25. bool search(char* word);
  26. void deleteTrie(Trie_node *root);
  27. private:
  28. Trie_node* root;
  29. };
  30.  
  31. Trie::Trie()
  32. {
  33. root = new Trie_node();
  34. }
  35.  
  36. void Trie::insert(const char* word)
  37. {
  38. Trie_node *location = root;
  39. while(*word)
  40. {
  41. if(location->next[*word-'a'] == NULL)//不存在则建立
  42. {
  43. Trie_node *tmp = new Trie_node();
  44. location->next[*word-'a'] = tmp;
  45. }
  46. location = location->next[*word-'a']; //每插入一步,相当于有一个新串经过,指针要向下移动
  47. word++;
  48. }
  49. location->isStr = true; //到达尾部,标记一个串
  50. }
  51.  
  52. bool Trie::search(char *word)
  53. {
  54. Trie_node *location = root;
  55. while(*word && location)
  56. {
  57. location = location->next[*word-'a'];
  58. word++;
  59. }
  60. return(location!=NULL && location->isStr);
  61. }
  62.  
  63. void Trie::deleteTrie(Trie_node *root)
  64. {
  65. for(i = ; i < branchNum; i++)
  66. {
  67. if(root->next[i] != NULL)
  68. {
  69. deleteTrie(root->next[i]);
  70. }
  71. }
  72. delete root;
  73. }
  74.  
  75. int main() //简单测试
  76. {
  77. Trie t;
  78. t.insert("a");
  79. t.insert("abandon");
  80. char* c = "abandoned";
  81. t.insert(c);
  82. t.insert("abashed");
  83. if(t.search("abashed"))
  84. printf("true\n");
  85. }

静态建树与动态建树的主要区别在于插入和删除操作。

插入操作:前者每次插入一个新节点当不存在相应字符时就利用实现已经创建好的数组存放,后者则动态申请一个节点。

删除操作:前者直接将根节点的next全部置为NULL即可,后者要释放所有动态申请的节点空间。

查询操作基本上一样。

然而动态分配内存和静态分配内存性能上存在显著不同!

静态分配会高效很多,但用了一些全局变量,不熟悉的情况下容易出错。熟悉了就没问题了。

动态分配,对于有多个测试实例,如果不释放动态分配的内存,可能导致MLE!

【Trie】模板(动态指针,静态数组)的更多相关文章

  1. "《算法导论》之‘队列’":队列的三种实现(静态数组、动态数组及指针)

    本文有关栈的介绍部分参考自网站数据结构. 1. 队列  1.1 队列的定义 队列(Queue)是只允许在一端进行插入,而在另一端进行删除的运算受限的线性表. (1)允许删除的一端称为队头(Front) ...

  2. "《算法导论》之‘栈’":栈的三种实现(静态数组、动态数组及指针)

    本文有关栈的介绍部分参考自网站数据结构. 1. 栈  1.1 栈的定义 栈(Stack)是限制仅在表的一端进行插入和删除运算的线性表. (1)通常称插入.删除的这一端为栈顶(Top),另一端称为栈底( ...

  3. C++中关于[]静态数组和new分配的动态数组的区别分析

    这篇文章主要介绍了C++中关于[]静态数组和new分配的动态数组的区别分析,很重要的概念,需要的朋友可以参考下 本文以实例分析了C++语言中关于[]静态数组和new分配的动态数组的区别,可以帮助大家加 ...

  4. C/C++静态数组与动态数组的区别

    简介 以下三行代码有什么区别? int a[10]; int *a = (int*)malloc(sizeof(int)*10); int *a = new int[10]; 第一行代码定义a为包含1 ...

  5. delphi中String 和 动态静态数组

    默认string类型为ansiString:有编译开关控制 shortString: strShort : shortString; strShort 大小256字节,可根据sizeof()计算出,s ...

  6. DelphiXe 中静态数组TByteArray和动态数组TBytes /array of byte 的区别

    在应用中发现静态数组和动态数组是有区别的: procedure TForm1.Button1Click(Sender: TObject);var  RsltStream: TMemoryStream; ...

  7. Spring MVC 学习总结(七)——FreeMarker模板引擎与动态页面静态化

    模板引擎可以让程序实现界面与数据分离,业务代码与逻辑代码的分离,这就提升了开发效率,良好的设计也使得代码复用变得更加容易.一般的模板引擎都包含一个模板解析器和一套标记语言,好的模板引擎有简洁的语法规则 ...

  8. JS 索引数组、关联数组和静态数组、动态数组

    JS 索引数组.关联数组和静态数组.动态数组 数组分类: 1.从数组的下标分为索引数组.关联数组 var ary1 = [1,3,5,8]; //按索引去取数组元素,从0开始(当然某些语言实现从1开始 ...

  9. c语言实现动态指针数组Dynamic arrays

    c语言实现动态数组.其它c的数据结构实现,hashTable參考点击打开链接 treeStruct參考点击打开链接 基本原理:事先准备好一个固定长度的数组. 假设长度不够的时候.realloc一块区域 ...

随机推荐

  1. ASP.NET MVC- 在Area里使用RedirectToAction跳转出错的解决方法

    mvc使用Area分区开发后,存在不同Area之间的跳转,需要为每个区间添加Area规则,如下: 按 Ctrl+C 复制代码 using System.Web.Mvc; namespace web.A ...

  2. JS:中文GB2312编码

    今天开发遇到了个问题,有点纳闷.... 在ajax的时候要传递一个中文值,不管我在js中是否使用了encodeURI.encodeURIComponent编码,但是在后台request获取的值是始终是 ...

  3. COM组件(ActiveX)控件注册失败

    这主要是由于旧版本的falsh player的原因,卸载干净并清理注册表,再次安装flash player即可. 具体方法: 1 在控制面板 卸载程序里面 卸载flash player 2 C:\WI ...

  4. css初始化代码方案

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

  5. 如何设置box shadow的透明度

    (从已经死了一次又一次终于挂掉的百度空间人工抢救出来的,发表日期2014-04-24) 今天发现使用box-shadow属性,可以很好的给div添加阴影效果,但是添加的效果如果是: -moz-box- ...

  6. PHP抓取豆瓣读书爬虫代码

    <?php//演示地址 http://asizu.sinaapp.com/reptile_douban.php//数据量不是特别大,没有写抓完数据便停止. 喜欢的朋友拿去自己改改就好了 head ...

  7. 防火墙没关导致 ORA-12541: TNS: 无监听程序

    电脑用着用着突然Oracle就报出下面的错误,按照网上的办法搞了几个小时都没有搞好. Oracle重装了好几次也没用,实在没办法又花了个多小时装了个虚机,结果也是同样的错误. 于是恍然大悟,可能是物理 ...

  8. Educational Codeforces Round 4 A. The Text Splitting 水题

    A. The Text Splitting 题目连接: http://www.codeforces.com/contest/612/problem/A Description You are give ...

  9. Java IO之File

    FILE类是用来实现获取文件.文件夹的类库工具,File并不是像类名所表示的那样仅仅是用来表示文件.它还能够用来表示文件夹. 所以能够用File来获取一个文件夹下的全部文件,甚至是文件夹中的文件. 一 ...

  10. Approaching the Fun Factor in Game Design

    I recently did some research on this and talked to Dr. Clayton Lewis (computer Scientist in Residenc ...