数据结构 -- Trie字典树
简介
字典树:又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。
优点:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
性质: 1. 根节点不包含字符,除根节点外每一个节点都只包含一个字符;
2. 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串;
3. 每个节点的所有子节点包含的字符都不相同。
应用场景:用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。
代码 实现
本文使用链表来实现Trie字典树,字符串的每个字符作为一个Node节点,Node主要有两部分组成:
- 是否是单词 (boolean isWord)
- 节点所有的子节点,用map来保存 (Map next)
添加
public void add(String word) { Node current = root; char[] cs = word.toCharArray(); for (char c : cs) { Node next = current.next.get(c); if (next == null) { //一个字符对应一个Node节点 current.next.put(c, new Node()); } current = current.next.get(c); } //current就是word的最后一个字符的Node //如果当前的node已经是一个word,则不需要添加 if (!current.isWord) { size++; current.isWord = true; } }
查找
Trie查找操作就比较简单了,遍历带查找的字符串的字符,如果每个节点都存在,并且待查找字符串的最后一个字符对应的Node的 isWord 属性为 true ,则表示该单词存在
public boolean contains(String word) { Node current = root; for (int i = 0; i < word.length(); i++) { char c = word.charAt(i); Node node = current.next.get(c); if (node == null) { return false; } current = node; } //current就是word的最后一个字符的Node return current.isWord; }
前缀查询
public boolean containsPrefix(String prefix) { Node current = root; for (int i = 0; i < prefix.length(); i++) { char c = prefix.charAt(i); Node node = current.next.get(c); if (node == null) { return false; } current = node; } return true; }
删除
Trie的删除操作就稍微复杂一些,主要分为以下3种情况:
1. 如果单词是另一个单词的前缀
如果待删除的单词是另一个单词的前缀,只需要把该单词的最后一个节点的 isWord 的改成false,比如Trie中存在 panda 和 pan 这两个单词,删除 pan ,只需要把字符 n 对应的节点的 isWord 改成 false 即可。
2. 如果单词的所有字母的都无分支,删除整个单词。
如果单词的所有字母的都没有多个分支(也就是说该单词所有的字符对应的Node都只有一个子节点),则删除整个单词。
3. 如果单词的除了最后一个字母,其他的字母有多个分支
public boolean remove(String word){ Node multiChildNode = null; int multiChildNodeIndex = -1; Node current = root; for (int i = 0; i < word.length(); i++) { Node child = current.next.get(word.charAt(i)); //如果Trie中没有这个单词 if (child == null) { return false; } //当前节点的子节点大于1个 if (child.next.size() > 1) { multiChildNodeIndex = i; multiChildNode = child; } current = child; } //如果单词后面还有子节点 if (current.next.size() > 0) { if (current.isWord) { current.isWord = false; size--; return true; } //不存在该单词,该单词只是前缀 return false; } //如果单词的所有字母的都没有多个分支,删除整个单词 if (multiChildNodeIndex == -1) { root.next.remove(word.charAt(0)); size--; return true; } //如果单词的除了最后一个字母,其他的字母有分支 if (multiChildNodeIndex != word.length() - 1) { multiChildNode.next.remove(word.charAt(multiChildNodeIndex + 1)); size--; return true; } return false; }
Trie查询效率非常高,但是对空间的消耗还是挺大的,这也是典型的空间换时间。
可以使用 压缩字典树(Compressed Trie) ,但是维护相对来说复杂一些。
如果我们不止存储英文单词,还有其他特殊字符,那么维护子节点的集合可能会更多。
可以对Trie字典树做些限制,比如每个节点只能有3个子节点,左边的节点是小于父节点的,中间的节点是等于父节点的,右边的子节点是大于父节点的,这就是三分搜索Trie字典树(Ternary Search Trie)。
参考:https://blog.csdn.net/johnny901114/article/details/80711441
原文地址:https://www.cnblogs.com/FondWang/p/11896425.html
- Java开发Spring笔记第二天
- PHP调用Go服务的正确方式 - Unix Domain Sockets
- 一条看似平常的报警邮件所做的分析(r8笔记第9天)
- 55. 上传文件(Web版) | 厚土Go学习笔记
- R语言与机器学习学习笔记(分类算法
- 54. 心跳的实现 | 厚土Go学习笔记
- 通过错误的sql来测试推理sql的解析过程(二) (r8笔记第7天)
- 53. Socket服务三次握手的示例 | 厚土Go学习笔记
- R分词继续,"不|知道|你在|说|什么"分词添加新词
- Java开发Spring第一天
- 关于R安装中文分词包安装不上的问题install.packages("tm")
- dataguard备库的数据文件的迁移实战(r8笔记第24天)
- Hive的left join、left outer join和left semi join三者的区别
- 52. Socket Server 自定义协议的简单实现 | 厚土Go学习笔记
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- JSP 中 out.print() 和 out.write() 区别
- IDEA 导入web项目
- IDEA 连接数据库
- 图解面试题:如何找到破产玩家?
- python面向对象学习(一)
- playbook中when的使用
- 当遇到跨域开发时, 我们如何处理好前后端配置和请求库封装(koa/axios版)
- 你想要的Android性能优化系列:内存优化 !
- phabricator介绍与搭建
- nginx反向代理与负载均衡
- APP | edxposed框架+trustmealredy模块抓包小程序
- nginx动态添加模块
- 曾经豪言“指哪爬哪”,如今被一个JS狠狠教做人
- mysql主从复制延迟问题记录
- linux下设置locale