【数据结构】Trie(字典树、前缀树)
什么是Trie?
Trie树,也叫作字典树或前缀树,顾名思义,它是一个树行结构。它是一种专门处理字符串匹配的数据结构,用来解决在一组字符串集合中快速查找某个字符串的问题。
它的核心思想就是通过最大限度地减少无畏的字符串比较,使得查询效率高效,即用空间换时间,再利用共同前缀来提高查询效率。
例如:通讯录
Trie存储结构如图:
每个节点有26个指向下个节点的指针(为了方便,这里就弄这几个节点)。节点代码如下:
public class Node{
char c;
Node next[26];
}
注意:
以上中并没有考虑到大小写问题,如果要包含大小写,就得把26个指针改成52个指针。
再比如如果存储更复杂的数据,例如存储网址,需要考虑到@、//、:等这些符号,正因为这个原因,想要设计一个更灵活的Trie,通常不会固定每一个节点只有26个指向下一个节点的指针,除非非常的肯定Trie处理的内容只包含小写的字母。
通常,只要每个节点右若干个指向下一个节点的指针,在这个描述里,只把26个指针这样的静态的数组描写成了若干,它背后其实就是一个动态的思想。
代码如下:
public class Node{
char c;
Map<char, Node> next;
}
接下来,我们再看另一个问题,Trie当中每个节点都包含一个字母。
但是,大家想象一下,其实从根节点找到下一个节点的过程中,我就已经知道这个字母已经是谁了,例如从根节点搜索cat这个词,之所以能够来到c节点,是因为在根节点就知道了下一个要到c字母所在的节点,所以,更准确的来讲我们应该把标在边上,我们是来到这个节点之前就已经知道了这个字母是什么了,才可能通过映射来找到下一个节点。所以,在节点实现中每一个节点可以不存储这个节点的值是没有任何问题的。
修改后的节点代码:
public class Node{
Map<char, Node> next;
}
还有一个问题,在Trie中查询一个单词从根节点出发到叶子节点,到了叶子节点就到了单词的地方,在这里,把叶子节点标蓝。
上图中,如果到了叶子节点t就找到了cat这个词,如果到了g就找到了dog这个词,以此类推。不过,在英语的世界中,很多的单词可能是另外单词的前缀。
什么意思呢?例如平底锅pan这个单词,如果Trie中既要存储pan这个单词,还要存储panda这个单词。此时,对于pan这个单词最后一个字母n,它并不是一个叶子节点,不然的话,就没法存储panda这个单词了。
正因为如此,在节点中需要一个标识,这个标识表达的意思就是当前的这个节点是否是一个单词的结尾,单词的结尾只靠叶子节点是区分不出来的。
修改后的节点代码:
public class Node{
boolean isWord;
Map<char, Node> next;
}
Trie的实现
1、插入元素
插入的过程非常简单,就是把要插入的元素从头到尾挨个取出来,如果存在这个元素,那么就在next中查到这个元素的下一个节点,如果不存在,添加到next中。注意:到单词结尾时需要把isWord设置为true。
2、查询元素
查询和插入的过程类似,把待查询的元素从头到尾挨个取出来,如果不存在,直接返回false。如果存储这个元素,那么就在next中查到这个元素的下一个节点,最后返回isWord。
3、前缀查询
Trie也叫作前缀树,这是为什么呢?
就是因为,在Trie中可以非常简单的去搜索某一个单词对应的前缀。
搜索过程其实非常简单,Trie存储结构:
从根节点开始向下搜索的过程其实都是在搜索单词的前缀,例如查找单词cat,找到c,c就是cat的前缀,再找到a,ca就是cat的前缀,以此类推。
所以在Trie中搜索一个单词的过程中,一路上所经过的字符串都是目标单词的前缀,正是因为这个原因,Trie也叫作前缀树。
4、代码实现
import java.util.TreeMap;
/**
* 描述:Trie树(字典树、前缀树)
* <p>
* Create By ZhangBiao
* 2020/5/16
*/
public class TrieTree {
private Node root;
private int size;
public TrieTree() {
this.root = new Node();
this.size = 0;
}
/**
* 获取Trie树中存储的单词数量
*
* @return
*/
public int getSize() {
return size;
}
/**
* 向Trie树中添加一个新的单词word
*
* @param word
*/
public void add(String word) {
Node cur = root;
for (int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
if (cur.next.get(c) == null) {
cur.next.put(c, new Node());
}
cur = cur.next.get(c);
}
if (!cur.isWord) {
cur.isWord = true;
size++;
}
}
/**
* 查询单词word是否在Trie树中
*
* @param word
* @return
*/
public boolean contains(String word) {
Node cur = root;
for (int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
if (cur.next.get(c) == null) {
return false;
}
cur = cur.next.get(c);
}
return cur.isWord;
}
/**
* 查询是否在Trie树中有单词以prefix为前缀
*
* @param prefix
* @return
*/
public boolean isPrefix(String prefix) {
Node cur = root;
for (int i = 0; i < prefix.length(); i++) {
char c = prefix.charAt(i);
if (cur.next.get(c) == null) {
return false;
}
cur = cur.next.get(c);
}
return true;
}
private class Node {
public boolean isWord;
public TreeMap<Character, Node> next;
public Node(boolean isWord) {
this.isWord = isWord;
this.next = new TreeMap<>();
}
public Node() {
this(false);
}
}
}
原文地址:https://www.cnblogs.com/zhangbiao97/p/12900100.html
- libphonenumber--windows上编译libphonenumber.lib以及使用(C++、VS2015)
- querySelector与querySelectorAll
- 【技术博客】Spark性能优化指南——基础篇
- 移动端兼容系列 HTML与CSS兼容
- 保存到配置文件
- linux学习第六十篇:Linux监控平台介绍,zabbix监控介绍,安装zabbix,忘记Admin密码如何做
- 支持向量机及Python代码实现
- 【技术博客】Android自定义Lint实践
- UC浏览器皮肤的那个坑
- 文本溢出-超出文本显示为省略号
- 征信区块链解决方案探索(Hyperledger)
- 概率论12 矩与矩生成函数
- Python的hasattr() getattr() setattr() 函数使用方法详解
- 查找字符串中出现最多的字符
- 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 数组属性和方法
- SAP Spartacus delivery mode continue button enable与否的逻辑
- codeforces 1342C(前缀和)
- leetcode-寻找两个正序数组的中位数
- Shell脚本常用命令一览
- 深入学习 React 合成事件
- 关于fixture.debugElement.query(By.css)这个方法的一个疑问
- codeforces 1334C(前缀和)
- Angular通过依赖注入机制注入一个对象的例子
- 简易数据分析(七):Web Scraper 抓取表格、分页器翻页数据
- codeforces 1327C(构造)
- zabbix分布式及高可用
- Angular单元测试的spyOn使用一例
- SQL-JOIN全解析
- Node.js上传单文件和多文件的一些示例博客和源代码
- ES6中的箭头函数=>