前言

字典树又称单词查找树,它是一种树形结构,是一种哈希树的变种,典型应用是用于统计,保存大量的字符串(但不仅限于字符串),统计以是否有以某字符串最为前缀的字符串,有的话有多少,某字符串出现了多少次等,所以经常被搜索引擎系统用于文本词频统计。

它与字典很相似,当你要查一个单词是不是在字典树中,首先看单词的第一个字母是不是在字典的第一层,如果不在,说明字典树里没有该单词,如果在就在该字母的孩子节点里找是不是有单词的第二个字母,没有说明没有该单词,有的话用同样的方法继续查找.字典树不仅可以用来储存字母,也可以储存数字等其它数据。它的优势是,利用字符串的公共前缀来节约存储空间,最大限度地减少无谓的字符串比较,查询效率比哈希表还高,当数据足够庞大时,会发现她比传统的字符串统计要快很多。


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

下边我将把我实现的代码和大家分享一下,代码几乎每行都有详细注释,大家一看就清除明了了,时间原因,就不再用多余的文字再追加详述了。

创建字典树

[java] view plaincopy
  1. package Trie;
  2. import org.apache.commons.lang3.StringUtils;
  3. import com.google.common.base.CharMatcher;
  4. /**
  5. * 字典树类
  6. *
  7. * @author chenleixing
  8. */
  9. public class Trie {
  10. //各个节点的子树数目即字符串中的字符出现的最多种类
  11. private final int SIZE = 26;
  12. //除根节点外其他所有子节点的数目
  13. private int numNode;
  14. //树的深度即最长字符串的长度
  15. private int depth;
  16. //字典树的根
  17. private TrieNode root;
  18. /**
  19. * 初始化字典树
  20. */
  21. public Trie() {
  22. this.numNode=0;
  23. this.depth=0;
  24. this.root = new TrieNode();
  25. }
  26. /**
  27. * 字典树节点类,为私有内部类
  28. */
  29. private class TrieNode {
  30. // 所有的儿子节点或者一级子节点
  31. private TrieNode[] son;
  32. // 有多少字符串经过或到达这个节点,即节点字符出现的次数
  33. private int numPass;
  34. // 有多少字符串通过这个节点并到此结束的数量
  35. private int numEnd;
  36. // 是否有结束节点
  37. private boolean isEnd;
  38. // 节点的值
  39. private char value;
  40. /**
  41. * 初始化节点类
  42. */
  43. public TrieNode() {
  44. this.numPass=0;
  45. this.numEnd=0;
  46. this.son = new TrieNode[SIZE];
  47. this.isEnd = false;
  48. }
  49. }

首先各种方法操作方法的字符串验证或非法判断

一般字典树常用存储单词类字符串,当然也可以存储数字或者其他字符只是一个耗费内存较多的问题,不管怎么最好在操作方法之前做个验证和判断,以更加的提高操作效率,代码健壮性更强,如插入,查找是否存在等,如果字符串中存在“非法”的字符,那么可以直接返回false来结束操作。代码如下边所示:

[java] view plaincopy
  1. /**
  2. * 对操作的字符串进行一个非法的判断,是否为字母构成的字符串
  3. */
  4. private boolean isStrOfLetter(String str){
  5. //null或者空白字符串,则插入失败
  6. if (StringUtils.isBlank(str)){
  7. return false;
  8. }
  9. //如果字符串中有非字母字符,则插入失败
  10. if(!CharMatcher.JAVA_LETTER.matchesAllOf(str)){
  11. return false;
  12. }
  13. return true;
  14. }

其中代码中我用到了common-lang工具包里的StringUtils工具类和Guava里的CharMatcher,当然大家可以用循环啊用正则表达式或者其他工具来实现相同的功能,这里就不再一一详述了,如果有想深入了解它们用法的话或jar包下载,可参考我的以下博文:commons-lang中常用方法,StringUtils方法全集介绍,JavaScript、Java正则表达式详解,打了兴奋剂的CharMatcher,Strings字符串判断工具。

字典树存储字符串方法实现

[java] view plaincopy
  1. /**
  2. * 插入方法,插入字符串,不区分大小写
  3. */
  4. public boolean insertStr(String str) {
  5. //插入的字符为非法字符,则插入失败
  6. if(!isStrOfLetter(str)){
  7. return false;
  8. }
  9. //插入字符串
  10. str=str.toLowerCase();//不区分大小写,转为小写
  11. char[] letters = str.toCharArray();//转成字符数组
  12. TrieNode node=this.root;//先从父节点开始
  13. for (char c:letters) {
  14. int pos = c - 'a';//得到应存son[]中的索引
  15. if (node.son[pos] == null) {//此字符不存在
  16. node.son[pos] = new TrieNode();
  17. node.son[pos].value = c;
  18. node.son[pos].numPass=1;
  19. this.numNode++;
  20. } else {//此字符已经存入
  21. node.son[pos].numPass++;
  22. }
  23. node = node.son[pos];//继续为下一下字符做准备
  24. }
  25. node.isEnd = true;//标记:有字符串到了此节点已结束
  26. node.numEnd++;//这个字符串重复次数
  27. if(letters.length>this.depth){//记录树的深度
  28. this.depth=letters.length;
  29. }
  30. return true;//插入成功
  31. }

查找是否存以某前缀开头的字符串方法实现

[java] view plaincopy
  1. /**
  2. * 在字典树中查找是否存在某字符串为前缀开头的字符串(包括前缀字符串本身),不区分大小写
  3. */
  4. public boolean isContainPrefix(String str) {
  5. //查找的字符是否非法字符,则失败
  6. if(!isStrOfLetter(str)){
  7. return false;
  8. }
  9. //查找字符串
  10. str=str.toLowerCase();//不区分大小写,转为小写
  11. char[] letters = str.toCharArray();//转成字符数组
  12. TrieNode node=this.root;//先从父节点开始
  13. for (char c:letters) {
  14. int pos = c - 'a';//得到应存son[]中的索引
  15. if (node.son[pos] != null) {
  16. node=node.son[pos];//此字符存在继续查找下一个字符
  17. } else {//此字符不存在
  18. return false;
  19. }
  20. }
  21. return true;
  22. }

查找是否存在某字符串(不为前缀)方法实现

[java] view plaincopy
  1. /**
  2. * 在字典树中查找是否存在某字符串(不为前缀),不区分大小写
  3. */
  4. public boolean isContainStr(String str) {
  5. //查找的字符是否非法字符,则失败
  6. if(!isStrOfLetter(str)){
  7. return false;
  8. }
  9. //查找字符串
  10. str=str.toLowerCase();//不区分大小写,转为小写
  11. char[] letters = str.toCharArray();//转成字符数组
  12. TrieNode node=this.root;//先从父节点开始
  13. for (char c:letters) {
  14. int pos = c - 'a';//得到应存son[]中的索引
  15. if (node.son[pos] != null) {
  16. node=node.son[pos];//此字符存在继续查找下一个字符
  17. } else {//此字符不存在
  18. return false;
  19. }
  20. }
  21. return node.isEnd;
  22. }

统计以指定字符串为前缀的字符串数量方法实现

[java] view plaincopy
  1. /**
  2. * 统计以指定字符串为前缀的字符串数量,不区分大小写
  3. */
  4. public int countPrefix(String str) {
  5. //统计的字符是否非法字符,则返回0
  6. if(!isStrOfLetter(str)){
  7. return 0;
  8. }
  9. //查找字符串
  10. str=str.toLowerCase();//不区分大小写,转为小写
  11. char[] letters = str.toCharArray();//转成字符数组
  12. TrieNode node=this.root;//先从父节点开始
  13. for (char c:letters) {
  14. int pos = c - 'a';//得到应存son[]中的索引
  15. if (node.son[pos] == null) {
  16. return 0;//没有以此字符串为前缀开头
  17. } else {//此字符存在,继续遍历
  18. node=node.son[pos];
  19. }
  20. }
  21. return node.numPass;
  22. }

统计某字符串出现的次数方法实现

[java] view plaincopy
  1. /**
  2. * 统计以指定字符串为前缀的字符串数量,不区分大小写
  3. */
  4. public int countPrefix(String str) {
  5. //统计的字符是否非法字符,则返回0
  6. if(!isStrOfLetter(str)){
  7. return 0;
  8. }
  9. //查找字符串
  10. str=str.toLowerCase();//不区分大小写,转为小写
  11. char[] letters = str.toCharArray();//转成字符数组
  12. TrieNode node=this.root;//先从父节点开始
  13. for (char c:letters) {
  14. int pos = c - 'a';//得到应存son[]中的索引
  15. if (node.son[pos] == null) {
  16. return 0;//没有以此字符串为前缀开头
  17. } else {//此字符存在,继续遍历
  18. node=node.son[pos];
  19. }
  20. }
  21. return node.numPass;
  22. }

前序遍历字典树方法实现

[java] view plaincopy
  1. /**
  2. * 统计以指定字符串为前缀的字符串数量,不区分大小写
  3. */
  4. public int countPrefix(String str) {
  5. //统计的字符是否非法字符,则返回0
  6. if(!isStrOfLetter(str)){
  7. return 0;
  8. }
  9. //查找字符串
  10. str=str.toLowerCase();//不区分大小写,转为小写
  11. char[] letters = str.toCharArray();//转成字符数组
  12. TrieNode node=this.root;//先从父节点开始
  13. for (char c:letters) {
  14. int pos = c - 'a';//得到应存son[]中的索引
  15. if (node.son[pos] == null) {
  16. return 0;//没有以此字符串为前缀开头
  17. } else {//此字符存在,继续遍历
  18. node=node.son[pos];
  19. }
  20. }
  21. return node.numPass;
  22. }

这里是通过递归打印出所有的节点的值,如果想存入一个List或者追加StringBuilder或者StringBuffer中,需要创建一个全局变量或者方法里创建然后以参数形式传到递归方法中,这里不再进行详述,因为字典树的主要用途不在这里,此方法一般不需要。

返回字典树的根节点

[java] view plaincopy
  1. /**
  2. * 返回根节点,根节点不存值
  3. */
  4. public TrieNode getRoot() {
  5. return this.root;
  6. }

返回字典树的深度

[java] view plaincopy
  1. /**
  2. * 返回字典树的深度
  3. */
  4. public int getDept() {
  5. return this.depth;
  6. }

返回字典树的所有子节点的数目

[java] view plaincopy
  1. /**
  2. * 返回字典树的所有子节点的数目(不包含子节点)
  3. */
  4. public int getNumNode() {
  5. return this.numNode;
  6. }

测试字典树的所有方法

[java] view plaincopy
  1. package Trie;
  2. import org.junit.Test;
  3. public class TrieTest {
  4. /**
  5. * 测试字典树
  6. *
  7. * @author chenleixing
  8. */
  9. @Test
  10. public void testTrie(){
  11. //创建一个字典树(其实可以在创建时指定字典树各节点的大小,大小根据存入字符种类的数量)
  12. Trie trie=new Trie();
  13. //测试字符串(当然越庞大越能展现它的优势)
  14. String[] testStrs=new String[]{"chefsd","chen","hahi","ch","cxing","hahha","my","home"};
  15. for(String s:testStrs){//向字典树中存入字符串
  16. trie.insertStr(s);
  17. }
  18. //测试是否包含指定前缀的字符串
  19. boolean isCont=trie.isContainPrefix("ch");
  20. System.out.println(isCont);//输出true
  21. //测试包含指定前缀的字符串的数量
  22. int countPrefix=trie.countPrefix("ch");
  23. System.out.println(countPrefix);//输出3
  24. //测试包含指定字符串的数量
  25. int countStr=trie.countStr("ch");
  26. System.out.println(countStr);//输出1
  27. //测试包含指定前缀的字符串的数量
  28. int countPre=trie.countPrefix("chee");
  29. System.out.println(countPre);//输出0
  30. //测试子节点的数量和树的深度
  31. int numNode=trie.getNumNode();//为22
  32. int dept=trie.getDept();//为6
  33. System.out.println("字典树子节点的数量:"+numNode+"  树的深度:"+dept);
  34. }
  35. }

测试结果

[java] view plaincopy
  1. true
  2. 3
  3. 1
  4. 0
  5. 字典树子节点的数量:22  树的深度:6

over了!

转载请注明—作者:Java我人生(陈磊兴)   原文出处:http://blog.csdn.net/chenleixing/article/details/44708533

最后,认真看过的网友们,大神们,如有感觉我这个程序猿有哪个地方说的不对或者不妥或者你有很好的提议或者建议或点子方法,还望您大恩大德施舍n秒的时间留下你的宝贵文字(留言),以便你,我,还有广大的程序猿们更快地成长与进步.......

字典树-大量字符串前缀及出现次数是否存在统计(Trie树-java)算法实现相关推荐

  1. 1375. 二进制字符串前缀一致的次数-前序遍历法

    1375. 二进制字符串前缀一致的次数 给你一个长度为 n .下标从 1 开始的二进制字符串,所有位最开始都是 0 .我们会按步翻转该二进制字符串的所有位(即,将 0 变为 1). 给你一个下标从 1 ...

  2. 巧用 Trie 树,实现搜索引擎关键词提示功能

    来源 | 码海 责编 | Carol 封图 | CSDN 付费下载于视觉中国 我们几乎每天都在用搜索引擎搜索信息,相信大家肯定有注意过这样一个细节:当输入某个字符的时候,搜索引框底下会出现多个推荐词, ...

  3. android搜索框功能实现_巧用 Trie 树,实现搜索引擎关键词提示功能

    来源 | 码海责编 | Carol封图 | CSDN 付费下载于视觉中国我们几乎每天都在用搜索引擎搜索信息,相信大家肯定有注意过这样一个细节:当输入某个字符的时候,搜索引框底下会出现多个推荐词,如下, ...

  4. 实现Trie树(C++)

    文章目录 前言 一.Trie树原理 二.Trie树实现(C++) 1.接口 2.实现 总结 前言 Trie树,又叫前缀树,字典树,单词查找树,是由二叉树衍生出来的一种树形高级数据结构.经常用于处理字符 ...

  5. 数据结构与算法之美笔记——基础篇(下):图、字符串匹配算法(BF 算法和 RK 算法、BM 算法和 KMP 算法 、Trie 树和 AC 自动机)

    图 如何存储微博.微信等社交网络中的好友关系?图.实际上,涉及图的算法有很多,也非常复杂,比如图的搜索.最短路径.最小生成树.二分图等等.我们今天聚焦在图存储这一方面,后面会分好几节来依次讲解图相关的 ...

  6. 【转】从Trie树(字典树)谈到后缀树

    本文第一部分,咱们就来了解这个Trie树,然后自然而然过渡到第二部分.后缀树,接着进入第三部分.详细阐述后缀树的构造方法-Ukkonen. 第一部分.Trie树 1.1.什么是Trie树 Trie树, ...

  7. 从Trie树(字典树)和后缀树

    从Trie树(字典树)谈到后缀树 转载:http://blog.csdn.net/v_july_v/article/details/6897097#t22 感谢作者,侵删. 引言 常关注本blog的读 ...

  8. 0x16.基本数据结构 — Trie树(字典树)+ A C 自 动 机

    目录 用TrieTrieTrie树来处理整数异或问题是真的舒服! 一.TrieTrieTrie树 TrieTrieTrie的基本操作 0.初始化 1.插入 2.检索 二.TrieTrieTrie树例题 ...

  9. 字符串匹配算法(Trie树)

    文章目录 1. Trie树概念 2. Trie树操作 2.1 存储 2.2 查找 2.3 插入 2.4 删除 2.5 打印 3. 完整代码 4. Trie树与散列表.红黑树的比较 4.1 思考题 参考 ...

  10. java单词匹配算法_前端学数据结构与算法(八): 单词前缀匹配神器-Trie树的实现及其应用...

    前言 继二叉树.堆之后,接下来介绍另外一种树型的数据结构-Trie树,也可以叫它前缀树.字典树.例如我们再搜索引擎里输入几个关键字之后,后续的内容会自动续上.此时我们输入的关键词也就是前缀,而后面的就 ...

最新文章

  1. Windows Azure 解决方案系列:组合拍卖供应商以云服务快速拓展,并节省成本
  2. python导入excel数据-python + Excel数据读取(更新)
  3. 【Groovy】自定义 Xml 生成器 BuilderSupport ( setParent 方法中设置父节点与子节点关系 )
  4. 一个有趣的算老鼠程序
  5. python sqlite数据库_用Python进行SQLite数据库操作
  6. 想学好C语言?先把基础打好再说吧!
  7. 地板之间出现缝隙如何处理?
  8. python自学行吗-自学python有用吗?
  9. 取消IE“已限制此网页运行可以访问计算机的脚本
  10. 使用Python对Syslog信息进行分析并绘图
  11. Shiro实现登录功能
  12. 设计模式23篇(VIP典藏版)
  13. c语言编程分数化简,C语言编程实例:将真分数分解为埃及分数
  14. python自回归模型_向量自回归模型(VAR)到底厉害在哪里?
  15. 查看linux主机防火墙列表,linux防火墙状态查看的方法实例
  16. Java ZIP压缩 ZipArchiveEntry实现ZIP高效、Java多线程压缩、可控CPU使用率 Apache commons-compress
  17. 美国 计算机与艺术 专业,美国加州大学圣地亚哥分校计算机与艺术专业.pdf
  18. 2020年起重机司机(限门式起重机)多少分及格及起重机司机(限门式起重机)考试内容
  19. 计算机科学与技术专业导论3500,计算机科学与技术专业导论.docx
  20. IDEA Maven工程出现org.codehaus.plexus.component.repository.exception.ComponentLookupException错误

热门文章

  1. 在ios中制作3d文字球效果
  2. Dev gridView中设置自适应列宽和日期显示格式、金额的显示格式
  3. win8下IE10停止工作解决办法
  4. gcc g++ 参数
  5. 设计模式练习:Composite模式
  6. 利用EXP/IMP进行数据迁移,如何转换表空间操作(完整版)
  7. Django Restful API Class Based View
  8. 关于MPMoviePlayerController类播放视频时,外放没有声音的问题(ios)
  9. 在 lamp(centos)下配置二级 域名 、虚拟主机
  10. async和await浅析