前缀树是什么 前缀树的使用场景
前缀树的概述
前缀树又名字典树,单词查找树,Trie树,是一种多路树形结构,是哈希树的变种,和hash效率有一拼,是一种用于快速检索的多叉树结构。
典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。
Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
Trie树也有它的缺点,Trie树的内存消耗非常大。
性质:不同字符串的相同前缀只保存一份。
操作:查找,插入,删除。
举个栗子:给出一组单词,inn, int, at, age, adv,ant, adv 我们可以得到下面的Trie:
从上面可以发现一些Trie树的特性:
1)根节点不包含字符,除根节点外的每一个子节点都包含一个字符。
2)从根节点到某一节点的路径上的字符连接起来,就是该节点对应的字符串。
3)每个节点的所有子节点包含的字符都不相同。
4)每条边对应一个字母。每个节点对应一项前缀。叶节点对应最长前缀,即单词本身。
单词inn与单词int有共同的前缀“in”, 因此他们共享左边的一条分支,root->i->in。同理,ate, age, adv, 和ant共享前缀"a",所以他们共享从根节点到节点"a"的边。
查询操纵非常简单。比如要查找int,顺着路径i -> in -> int就找到了。
搭建Trie的基本算法也很简单,无非是逐一把每则单词的每个字母插入Trie。插入前先看前缀是否存在。如果存在,就共享,否则创建对应的节点和边。比如要插入单词add,就有下面几步:
考察前缀"a",发现边a已经存在。于是顺着边a走到节点a。
考察剩下的字符串"dd"的前缀"d",发现从节点a出发,已经有边d存在。于是顺着边d走到节点ad
考察最后一个字符"d",这下从节点ad出发没有边d了,于是创建节点ad的子节点add,并把边ad->add标记为d。
前缀树的应用场景
(1)字符串的快速检索
字典树的查询时间复杂度是O(logL),L是字符串的长度。所以效率还是比较高的。字典树的效率比hash表高。
hash表:
通过hash函数把所有的单词分别hash成key值,查询的时候直接通过hash函数即可,都知道hash表的效率是非常高的为O(1),当然这是对于如果我们hash函数选取的好,计算量少,且冲突少,那单词查询速度肯定是非常快的。那如果hash函数的计算量相对大呢,且冲突律高呢?这些都是要考虑的因素。
还有就是hash表不支持动态查询,什么叫动态查询,当我们要查询单词apple时,hash表必须等待用户把单词apple输入完毕才能hash查询。当你输入到appl时肯定不可能hash吧。
字典树(tries树):
对于单词查询这种,还是用字典树比较好,但也是有前提的,空间大小允许,字典树的空间相比较hash还是比较浪费的,毕竟hash可以用bit数组。
(2)字符串排序
从上图我们很容易看出单词是排序的,先遍历字母序在前面。
减少了没必要的公共子串。
(3)最长公共前缀
inn和int的最长公共前缀是in,遍历字典树到字母n时,此时这些单词的公共前缀是in。
(4)自动匹配前缀显示后缀
我们使用辞典或者是搜索引擎的时候,输入appl,后面会自动显示一堆前缀是appl的东东吧。
那么有可能是通过字典树实现的,前面也说了字典树可以找到公共前缀,我们只需要把剩余的后缀遍历显示出来即可。
前缀树的java实现
节点
import java.util.Arrays;public class TreeNode{//经过这个节点的字符串的个数(以这个节点为前缀的字符串的个数)public int path;//以这个节点结束的字符串的个数(有多少个字符串有这条路径的char组成)public int end;//对应着小写的a-z的26个字母(如果要更多可以使用hashmap<char,Node>public TreeNode[] next;public TreeNode(){path = 0;end = 0;next = new TreeNode[26];}@Overridepublic String toString() {return "TreeNode{" +"path=" + path +", end=" + end +", next=" + Arrays.toString(next) +'}';}
}
前缀树(增加,查询字符串数量,查询前缀数量)
package datastructure.tree.trietree;public class TrieTree {public TreeNode root;public TrieTree(){root=new TreeNode();}/**在前缀树中插入字符串* 这种++的方法,导致,一个node,有多少个end,就有多少个相同的字符串* 一个node,有多少个path,就有多少个字符串经过(root的path代表有多少个字符串)(字符串末尾的node的path也会++)* @param string 被插入的字符串(以前插入过的也可以插入)*/public void insertString(String string){if(string==null||string.length()==0){return;}int length=string.length();TreeNode nowNode=root;for(int i=0;i<length;i++){char now=string.charAt(i);int index=now-'a';//index为字符now所处的位置if(nowNode.next[index]==null){nowNode.next[index]=new TreeNode();}//先对当前node的path++,再转移到下一个nodenowNode.path++;nowNode=nowNode.next[index];}//在最后的node,path和end++nowNode.path++;nowNode.end++; }/**返回这个前缀树总共插入了多少个字符串* @return*/public int size(){return root.path;}/**前缀树查询总共插入这个字符串多少次,如果没插入过,则返回0* @param string* @return*/public int getStringNum(String string){if(string==null||string.length()==0){return 0;}int length=string.length();TreeNode nowNode=root;for(int i=0;i<length;i++){char now=string.charAt(i);int index=now-'a';//如果没有这个节点,说明不存在,直接返回0if(nowNode.next[index]==null){return 0;}nowNode=nowNode.next[index];} //此时nowNode已经处于最后一个节点return nowNode.end; }/**前缀树查询以这个字符串为前缀的字符串总共多少个(包括以他为结尾的)* @param string 前缀* @return*/public int getPrefixNum(String string){if(string==null||string.length()==0){return 0;}int length=string.length();TreeNode nowNode=root;for(int i=0;i<length;i++){char now=string.charAt(i);int index=now-'a';//如果没有这个节点,说明前缀不存在,直接返回0if(nowNode.next[index]==null){return 0;}nowNode=nowNode.next[index];} //此时nowNode已经处于前缀的最后一个节点return nowNode.path; }}
测试:
package datastructure.tree.trietree;public class Main {public static void main(String[] args) {TrieTree tree=new TrieTree();tree.insertString("aa");tree.insertString("aa");tree.insertString("ab");tree.insertString("ba");//System.out.println(tree.root);//System.out.println(tree.size());//System.out.println(tree.getStringNum("aa"));//System.out.println(tree.getStringNum("ab"));//System.out.println(tree.getStringNum("ac"));System.out.println(tree.getPrefixNum("a"));System.out.println(tree.getPrefixNum("b"));System.out.println(tree.getPrefixNum("c"));}}
几百本常用电子书免费领取:https://github.com/XiangLinPro/IT_book
Life is not to surpass
others, but to surpass ourselves.
生命不是要超越别人,而是要超越自己。
前缀树是什么 前缀树的使用场景相关推荐
- 路径前缀是什么意思_Trie 树是什么样的数据结构?有哪些应用场景?
(给算法爱好者加星标,修炼编程内功) 作者:神奕 blog.csdn.net/lisonglisonglisong/article/details/45584721 [前言]在计算机科学中,trie, ...
- 【HDOJ6955】Xor sum(异或前缀和,01字典树)
1006 Xor sum 题意: 给出一个长度为n的序列,求一个最短连续子序列满足异或和大于等于k.n<1e5. 思路: 参考CF665E,求序列a中有多少个异或和大于等于k的子序列,枚举所有的 ...
- 前缀树(字典树,单词查找树,Trie树)
参考网址:https://blog.csdn.net/u013949069/article/details/78056102?utm_source=copy 概述 前缀树又名字典树,单词查找树,Tri ...
- 字典树(Trie/前缀树)
目录 字典树的概念 字典树的逻辑 字典树的实现 易混点剖析 代码示例 字典树的概念 字典树(Trie)是一种空间换时间的数据结构,是一棵关于"字典"的树.主要用于统计.排序和保存大 ...
- BZOJ 3483 SGU505 Prefixes and suffixes(字典树+可持久化线段树)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3483 [题目大意] 给出一些串,同时给出m对前缀后缀,询问有多少串满足给出的前缀后缀模 ...
- 模板 - 树上问题(树的直径、动态查询树的直径、树的重心)
整理的算法模板合集: ACM模板 目录 一.树的直径 树形DP 两次DFS / BFS(找到直径的两个端点) 二.动态修改树的边权并求每个时刻的直径(线段树) 三.树的重心 一.树的直径 树的直径满足 ...
- 解题报告:P3834 【模板】可持久化线段树 2(主席树)详解
P3834 [模板]可持久化线段树 2(主席树) 题解 P3834 [[模板]可持久化线段树 2(主席树)] 1)静态求第k大数 可持久化线段树,不能用堆的方法存子结点了,所以用指针l表示左儿子r表示 ...
- [学习笔记]圆方树广义圆方树
引入 偶尔,我们会遇到一些要在无向图/仙人掌上做的问题,这些问题如果在树上就会比较方便,那么我们就开始考虑能不能把原图等效成一棵树,然后就可以方便地乱搞了? 圆方树就是一种将无向图/仙人掌变成树的数据 ...
- COGS-257-动态排名系统-树状数组+主席树
描述 给定一个长度为N的已知序列A[i](1<=i<=N),要求维护这个序列,能够支持以下两种操作: 1.查询A[i],A[i+1],A[i+2],...,A[j](1<=i< ...
最新文章
- log4j2配置实例[按小时记录日志文件]
- 骚操作!用 CPU 烤肉,这位程序员做到了
- 从零开始学Win32平台缓冲区溢出(Part1)
- 苹果6sp内存可以扩展吗_苹果手机iPhone 12 mini能用6年吗?网友:可以
- php如何实现读取网易有道词典输出单词的xml格式并且转化为html形式
- Makefile的重建与include指令
- WebTrends Log Analyzer
- 50句看后可以变换心情豁然开朗的话
- Cpp 对象模型探索 / operator new、operator delete、operator new[] 和 operator delete [] 重载
- Spring Security OAuth2——自定义OAuth2第三方登录(Gitee)
- 页面滚动到指定class样式位置
- Nginx的启动阶段讲解
- azure云数据库_Azure Data Studio中Windows的数据库管理工具扩展
- 两天,我把分布式事务搞完了!
- 华为的创新——流程和组织结构
- java excel 导入导出_java中excel文件的导入和导出
- apache poi教程_Apache POI教程
- 推荐:绝对是最好用的公式编辑器
- python分支语句中if和else必须同时出现吗_多分支决策必须采用嵌套的if-else语句实现: A、 对 B、 错_学小易找答案...
- 0809 电子科学与技术一级学科简介