前缀树
时间:2021-08-14
本文章向大家介绍前缀树,主要包括前缀树使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
何为前缀树?如何生成前缀树?
例子:
一个字符串类型的数组arr1,另一个字符串类型的数组arr2。
arr2中有哪些字符,是arr1中出现的?请打印。
arr2中有哪些字符,是作为arr1中某个字符串前缀出现的?请打印。
arr2中有哪些字符,是作为arr1中某个字符串前缀出现的?请打印arr2中出现次数最大的前缀。
又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。
典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。
它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
前缀树节点
//前缀树节点 public static class TrieNode { public int path;//在加前缀树的时候,这个节点到达过(通过)多少次 public int end; //这个节点是否是一个字符串结尾的节点,如果是的话它是多少个字符串结尾的节点 public TrieNode[] nexts; //如果路(字符种类)不止26条,可以以使用HashMap<Char,Node> nexts;算法一样,coding细节会有不同 //如果希望路与路之间不是像HashMap分散表达,而是连着的,可以使用TreeMap来表示 public TrieNode() { path = 0; end = 0; // nexts[0] == null 没有走向'a'的路 // nexts[0] != null 有走向'a'的路 // ... // nexts[25] == null 没有走向'z'的路 // nexts[25] != null 有走向'z'的路 //提前建了26条路,通过看下级有没有节点就知道后面有没有路 nexts = new TrieNode[26]; } }
图解:
根节点上p=4:
1、表示有多少(4)个以空字符串为前缀的字符串
2、表示前缀树中一共有多少(4)个字符串
3、若根节点上e=0,表示前缀树中有1个空字符串
前缀树相关操作:
package Algorithms; public class TrieTree { //前缀树节点 public static class TrieNode { public int path;//在加前缀树的时候,这个节点到达过(通过)多少次 public int end; //这个节点是否是一个字符串结尾的节点,如果是的话它是多少个字符串结尾的节点 public TrieNode[] nexts; //如果路(字符种类)不止26条,可以以使用HashMap<Char,Node> nexts;算法一样,coding细节会有不同 //如果希望路与路之间不是像HashMap分散表达,而是连着的,可以使用TreeMap来表示 public TrieNode() { path = 0; end = 0; // nexts[0] == null 没有走向'a'的路 // nexts[0] != null 有走向'a'的路 // ... // nexts[25] == null 没有走向'z'的路 // nexts[25] != null 有走向'z'的路 //提前建了26条路,通过看下级有没有节点就知道后面有没有路 nexts = new TrieNode[26]; } } //前缀树 public static class Trie { private TrieNode root; //新建类的时候会初始化一个根节点 public Trie() { root = new TrieNode(); } public void insert(String word) { if (word == null) { return; } char[] chs = word.toCharArray(); // "abc" -> ['a', 'b', 'c'] TrieNode node = root; //node从根节点出发 node.path++; //来到根节点后,path = 1 int index = 0; for (int i = 0; i < chs.length; i++) { //从左往右遍历字符 index = chs[i] - 'a'; //由字符,对应走向哪条路 index = 'a'-'a'=0 (把字符转化为了ASCII码) if (node.nexts[index] == null) { //没有走向a的路 node.nexts[index] = new TrieNode(); //新建节点 } node = node.nexts[index]; //(如果有走向a的路)节点后移 node.path++; } node.end++; //来到字符串末尾,end++ } //删除某一个字符串:沿途p--,最后一个Node的e-- //若删除中发现当前节点的下一节点p值在--之后为0,则将当前节点的下一个节点置空 public void delete(String word) { if (search(word) != 0) { //确定树中有word才进行删除操作 char[] chs = word.toCharArray(); TrieNode node = root; int index = 0; for (int i = 0; i < chs.length; i++) { index = chs[i] - 'a'; if (--node.nexts[index].path == 0) {//当前节点下级节点的path在--之后变为0 node.nexts[index] = null; //把下级节点置空 return; } node = node.nexts[index]; //节点后移 } node.end--; } } //查询word这个字符串加入过几次 public int search(String word) { if (word == null) { return 0; } char[] chs = word.toCharArray(); TrieNode node = root; int index = 0; for (int i = 0; i < chs.length; i++) { index = chs[i] - 'a'; if (node.nexts[index] == null) { return 0; //字符没结束,node结束了:例如有"abcdefg",但没有"abc" } node = node.nexts[index]; } return node.end; //字符走完,返回末尾字符节点的end值 } //查询所有加入的字符串中,有几个是以pre这个字符串作为前缀的 public int prefixNumber(String pre) { if (pre == null) { return 0; } char[] chs = pre.toCharArray(); TrieNode node = root; int index = 0; for (int i = 0; i < chs.length; i++) { index = chs[i] - 'a'; if (node.nexts[index] == null) { return 0; } node = node.nexts[index]; } return node.path; } } public static void main(String[] args) { Trie trie = new Trie(); System.out.println(trie.search("zuo")); //0 trie.insert("zuo"); System.out.println(trie.search("zuo")); //1 trie.delete("zuo"); System.out.println(trie.search("zuo")); //0 trie.insert("zuo"); trie.insert("zuo"); trie.delete("zuo"); System.out.println(trie.search("zuo")); //1 trie.delete("zuo"); System.out.println(trie.search("zuo")); //0 trie.insert("zuoa"); trie.insert("zuoac"); trie.insert("zuoab"); trie.insert("zuoad"); trie.delete("zuoa"); System.out.println(trie.search("zuoa")); //0 System.out.println(trie.prefixNumber("zuo")); //3 } }
原文地址:https://www.cnblogs.com/zh-xiaoyuan/p/15139366.html
- 【Scikit-Learn 中文文档】新异类和异常值检测 - 无监督学习 - 用户指南 | ApacheCN
- Golang中使用echo框架、MongoDB、JWT搭建REST API
- 在对象的原型上添加方法?
- KMeans聚类算法思想与可视化
- 控制台断点调试
- 函数声明与表达式的区别
- 揭开身份证验证的神秘面纱
- return语句的用法
- JavaScript基础讲解 - 函数与参数
- Go实战--golang中使用RethinkDB(gorethink/gorethink.v3)
- 如何修改alert样式
- 用R语言做数据清理(详细教程)
- 如何用R语言从网上读取多样格式数据
- 深入剖析iframe跨域问题
- 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 数组属性和方法
- java写一个音乐播放程序,同时显示歌词
- 使用反射获取注解中的内容
- 记一次企业微信对接踩坑之旅(ಥ_ಥ)
- 图解 SQL,这也太形象了吧!
- 函数式接口小结
- 冒泡排序+选择排序+插入排序+图与代码
- [解锁新姿势] 分享 7 个优化代码的技巧
- 常量池与堆中放入String数据,比较大小
- Could not find artifact org.apache.tomcat.maven:tomcat7-maven-plugin:pom.....
- try{}catch{}finally{}运行后的执行结果
- MySQL死锁产生原因和解决方法
- 文件操作与文件夹操作
- 使用文件字节输入流FileInputStream读取文件
- 二进制与十进制与十六进制介绍+转换+图解
- Swagger 3.0 官方 starter 诞生了,其它的都可以扔了~