Trie树的基本实现
- 格式:doc
- 大小:111.00 KB
- 文档页数:9
Trie树/字典树的简介及实现Trie树|字典树的简介及实现1综述又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。
典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。
它的优点是:利用字符串的公共前缀来节约存储空间,最大限度地减少无谓的字符串比较,查询效率比哈希表高。
Trie树结构的优点在于:1)不限制子节点的数量;2)自定义的输入序列化,突破了具体语言、应用的限制,成为一个通用的框架;3)可以进行最大Tokens序列长度的限制;4)根据已定阈值输出重复的字符串;5)提供单个字符串频度查找功能;6)速度快,在两分钟内完成1998年1月份人民日报(19056行)的重复字符串抽取工作。
优点来源于Linux公社网站()/Linux/2012-04/57911.htm2性质它有3个基本性质:1) 根节点不包含字符,除根节点外每一个节点都只包含一个字符。
2) 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
3) 每个节点的所有子节点包含的字符都不相同。
3基本操作其基本操作有:查找、插入和删除,当然删除操作比较少见.我在这里只是实现了对整个树的删除操作,至于单个word的删除操作也很简单.4实现方法搜索字典项目的方法为:(1) 从根结点开始一次搜索;(2) 取得要查找关键词的第一个字母,并根据该字母选择对应的子树并转到该子树继续进行检索;(3) 在相应的子树上,取得要查找关键词的第二个字母,并进一步选择对应的子树进行检索。
(4) 迭代过程……(5) 在某个结点处,关键词的所有字母已被取出,则读取附在该结点上的信息,即完成查找。
其他操作类似处理5. Trie原理——Trie的核心思想是空间换时间。
利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
6.代码实现[cpp] view plaincopy1.const int branchNum = 26; //声明常量2.int i;3.4.struct Trie_node5.{6. boolisStr; //记录此处是否构成一个串。
字典树(Trie树)实现与应⽤⼀、概述 1、基本概念 字典树,⼜称为单词查找树,Tire数,是⼀种树形结构,它是⼀种哈希树的变种。
2、基本性质根节点不包含字符,除根节点外的每⼀个⼦节点都包含⼀个字符从根节点到某⼀节点。
路径上经过的字符连接起来,就是该节点对应的字符串每个节点的所有⼦节点包含的字符都不相同 3、应⽤场景 典型应⽤是⽤于统计,排序和保存⼤量的字符串(不仅限于字符串),经常被搜索引擎系统⽤于⽂本词频统计。
4、优点 利⽤字符串的公共前缀来减少查询时间,最⼤限度的减少⽆谓的字符串⽐较,查询效率⽐哈希树⾼。
⼆、构建过程 1、字典树节点定义class TrieNode // 字典树节点{private int num;// 有多少单词通过这个节点,即由根⾄该节点组成的字符串模式出现的次数private TrieNode[] son;// 所有的⼉⼦节点private boolean isEnd;// 是不是最后⼀个节点private char val;// 节点的值TrieNode(){num = 1;son = new TrieNode[SIZE];isEnd = false;}} 2、字典树构造函数Trie() // 初始化字典树{root = new TrieNode();} 3、建⽴字典树// 建⽴字典树public void insert(String str) // 在字典树中插⼊⼀个单词{if (str == null || str.length() == 0){return;}TrieNode node = root;char[] letters = str.toCharArray();//将⽬标单词转换为字符数组for (int i = 0, len = str.length(); i < len; i++){int pos = letters[i] - 'a';if (node.son[pos] == null) //如果当前节点的⼉⼦节点中没有该字符,则构建⼀个TrieNode并复值该字符{node.son[pos] = new TrieNode();node.son[pos].val = letters[i];}else//如果已经存在,则将由根⾄该⼉⼦节点组成的字符串模式出现的次数+1{node.son[pos].num++;}node = node.son[pos];}node.isEnd = true;} 4、在字典树中查找是否完全匹配⼀个指定的字符串// 在字典树中查找⼀个完全匹配的单词.public boolean has(String str){if(str==null||str.length()==0){return false;}TrieNode node=root;char[]letters=str.toCharArray();for(int i=0,len=str.length(); i<len; i++){int pos=letters[i]-'a';if(node.son[pos]!=null){node=node.son[pos];}else{return false;}}//⾛到这⼀步,表明可能完全匹配,也可能部分匹配,如果最后⼀个字符节点为末端节点,则是完全匹配,否则是部分匹配return node.isEnd;} 5、前序遍历字典树 // 前序遍历字典树.public void preTraverse(TrieNode node){if(node!=null){System.out.print(node.val+"-");for(TrieNode child:node.son){preTraverse(child);}}} 6、计算单词前缀的数量 // 计算单词前缀的数量public int countPrefix(String prefix){if(prefix==null||prefix.length()==0){return-1;}TrieNode node=root;char[]letters=prefix.toCharArray();for(int i=0,len=prefix.length(); i<len; i++){int pos=letters[i]-'a';if(node.son[pos]==null){return 0;}elsenode=node.son[pos];}}return node.num;} 完整代码:package com.xj.test;public class Trie{private int SIZE = 26;private TrieNode root;// 字典树的根class TrieNode // 字典树节点{private int num;// 有多少单词通过这个节点,即由根⾄该节点组成的字符串模式出现的次数private TrieNode[] son;// 所有的⼉⼦节点private boolean isEnd;// 是不是最后⼀个节点private char val;// 节点的值TrieNode(){num = 1;son = new TrieNode[SIZE];isEnd = false;}}Trie() // 初始化字典树{root = new TrieNode();}// 建⽴字典树public void insert(String str) // 在字典树中插⼊⼀个单词{if (str == null || str.length() == 0){return;}TrieNode node = root;char[] letters = str.toCharArray();//将⽬标单词转换为字符数组for (int i = 0, len = str.length(); i < len; i++){int pos = letters[i] - 'a';if (node.son[pos] == null) //如果当前节点的⼉⼦节点中没有该字符,则构建⼀个TrieNode并复值该字符 {node.son[pos] = new TrieNode();node.son[pos].val = letters[i];}else//如果已经存在,则将由根⾄该⼉⼦节点组成的字符串模式出现的次数+1{node.son[pos].num++;}node = node.son[pos];}node.isEnd = true;}// 计算单词前缀的数量public int countPrefix(String prefix){if(prefix==null||prefix.length()==0){return-1;}TrieNode node=root;char[]letters=prefix.toCharArray();for(int i=0,len=prefix.length(); i<len; i++){int pos=letters[i]-'a';if(node.son[pos]==null){return 0;}else{node=node.son[pos];}return node.num;}// 打印指定前缀的单词public String hasPrefix(String prefix){if (prefix == null || prefix.length() == 0){return null;}TrieNode node = root;char[] letters = prefix.toCharArray();for (int i = 0, len = prefix.length(); i < len; i++){int pos = letters[i] - 'a';if (node.son[pos] == null){return null;}else{node = node.son[pos];}}preTraverse(node, prefix);return null;}// 遍历经过此节点的单词.public void preTraverse(TrieNode node, String prefix){if (!node.isEnd){for (TrieNode child : node.son){if (child != null){preTraverse(child, prefix + child.val);}}return;}System.out.println(prefix);}// 在字典树中查找⼀个完全匹配的单词.public boolean has(String str){if(str==null||str.length()==0){return false;}TrieNode node=root;char[]letters=str.toCharArray();for(int i=0,len=str.length(); i<len; i++){int pos=letters[i]-'a';if(node.son[pos]!=null){node=node.son[pos];}else{return false;}}//⾛到这⼀步,表明可能完全匹配,可能部分匹配,如果最后⼀个字符节点为末端节点,则是完全匹配,否则是部分匹配return node.isEnd;}// 前序遍历字典树.public void preTraverse(TrieNode node){if(node!=null){System.out.print(node.val+"-");for(TrieNode child:node.son){preTraverse(child);}}}public TrieNode getRoot(){return this.root;}public static void main(String[]args){Trie tree=new Trie();String[]strs= {"banana","band","bee","absolute","acm",};String[]prefix= {"ba","b","band","abc",};for(String str:strs){tree.insert(str);}System.out.println(tree.has("abc"));tree.preTraverse(tree.getRoot());System.out.println();//tree.printAllWords();for(String pre:prefix){int num=tree.countPrefix(pre);System.out.println(pre+"数量:"+num);}}}View Code 执⾏结果截图:三、简单应⽤ 下⾯讲⼀个简单的应⽤,问题是这样的: 现在有⼀个英⽂字典(每个单词都是由⼩写的a-z组成),单词量很⼤,⽽且还有很多重复的单词。
Trie树高效存储和检索字符串集合Trie树,也称为字典树或者前缀树,是一种非常高效的数据结构,用于存储和检索字符串集合。
它通过利用字符串的公共前缀来减少存储空间,并且可以在O(k)的时间复杂度下查找字符串,其中k是字符串的长度。
本文将介绍Trie树的原理和应用,并展示它在存储和检索字符串集合方面的优势。
一、Trie树的原理Trie树的基本思想是通过将每个字符串拆分为字符节点,并将字符节点以树形结构连接起来,从而形成一个维护字符串集合的数据结构。
它的根节点不包含任何字符,每个子节点代表一个字符,并通过连接方式表示字符串的各个字符之间的关系。
在Trie树中,每个节点都可以包含一个或多个子节点,每个节点的子节点表示在该位置上可能存在的下一个字符。
通过遍历从根节点到叶子节点的路径,我们可以得到一个完整的字符串。
这种结构使得Trie树特别适用于存储大量的字符串,并且可以高效地检索它们。
二、Trie树的构建构建Trie树的过程需要遍历每个字符串,并将它们的字符按照顺序插入到树中。
首先创建一个根节点,然后遍历每个字符串,对于每个字符,我们检查它是否已经是当前节点的子节点。
如果是,则将当前节点更新为该子节点,并继续遍历下一个字符;如果不是,则创建一个新节点,并将其作为当前节点的子节点,然后更新当前节点为新创建的节点,并继续遍历下一个字符。
重复这个过程,直到遍历完所有的字符串。
三、Trie树的应用1. 单词查找由于Trie树可以高效地查找字符串,因此它在单词查找中得到了广泛的应用。
例如,在文本编辑器中,我们可以使用Trie树来实现自动完成功能,根据用户已输入的前缀快速找到匹配的单词。
另外,Trie 树还可以用于拼写检查和单词纠错等应用场景。
2. IP地址查找在网络领域,Trie树也被广泛应用于IP地址查找。
通过将IP地址拆分为多个部分,并构建相应的Trie树,我们可以快速检索到给定IP 地址所对应的地理位置或者网络信息。
二进制trie树算法二进制Trie树算法概述二进制Trie树算法是一种用于高效存储和查询二进制数据的数据结构。
它通过将二进制数据的每一位作为树的节点来构建树结构,从而实现了对二进制数据的快速检索和插入操作。
本文将介绍二进制Trie树的原理、构建过程、插入和查询操作以及相关的应用场景。
一、原理二进制Trie树是一种前缀树的变种,它的每个节点都有两个子节点,分别对应二进制的0和1。
对于一个二进制数据,根节点表示最高位,每向下一层则表示二进制的下一位。
当插入或查询数据时,从根节点开始,根据数据每一位的值选择相应的子节点,直到达到数据的最后一位或者遇到空节点。
二、构建过程构建二进制Trie树的过程是逐位插入二进制数据的过程。
从根节点开始,根据数据的每一位选择相应的子节点,如果子节点不存在,则新建一个子节点。
如果数据的所有位都插入完成,将最后一个节点标记为叶节点,表示该节点代表一个完整的二进制数据。
三、插入操作插入一个二进制数据的过程就是将该数据的每一位逐个插入到Trie树中的过程。
从根节点开始,根据数据的每一位选择相应的子节点,如果子节点不存在,则新建一个子节点。
如果数据的所有位都插入完成,将最后一个节点标记为叶节点。
四、查询操作查询一个二进制数据的过程是根据数据的每一位在Trie树中查找对应的子节点。
从根节点开始,根据数据的每一位选择相应的子节点,如果子节点不存在,则表示该二进制数据不存在于Trie树中。
如果遍历到了数据的最后一位,并且最后一个节点被标记为叶节点,表示该二进制数据存在于Trie树中。
五、应用场景二进制Trie树广泛应用于网络路由表、IP地址查找、字符串匹配等领域。
在网络路由表中,每个节点表示一个路由前缀,节点的子节点表示该前缀的下一跳路由。
通过二进制Trie树可以快速找到匹配的路由前缀,从而实现高效的数据包转发。
在IP地址查找中,可以将每个IP地址转换为二进制表示,然后通过二进制Trie树进行快速查找。
数据结构之trie树今天看了下trie树,总结下:1、trie树定义:trie树可以看做用位置来标记元素。
下图是一个trie树的例子:从图中可以知道,从跟节点开始遍历树的话,在一个路径上会生成单词(当然不一定遍历到叶子节点,图中是叶子节点才形成一个单词,但实际中并不一定这样的。
可以在路径中的某个子路径上形成一个单词,当然这样的话需要通过一定的标记来说明一个单词已经形成了)可以通过下面的结构来定义trie树:#define MAX_CHILD 26 //这里使用26,我们只用来分析小写的英文字母构成的trie树,不考虑数字等其他字符,如果要考虑这些的话,可以将MAX_CHILD定义为256,这样就包括了所有的ascii码了typedef struc trie_node{int count; //用于标记在该节点是否可以形成一个单词;如果count不等于0则表示从跟节点到该节点的路径构成一个单词struct trie_node *child[MAX_CHILD];//用来标记孩子节点,例如child[0]就表示字符’a’;child[1]表示‘b’等。
这里就可以看作是用位置来表示一个字符,如果该位置的指针不为空,那么就表示该字符是存在的。
}node, *tree;考虑下图的话:对于根节点,他的第0、1、4、7个子节点是不为空的,表示字符a、b、e、h是在trie树中的。
同理对于子节点a来说,他的第1个子节点是不为空的,因此字符b在子节点a构成的trie 树中的。
其他的同理。
这样的话就表示字符串abcd是在trie树中的。
等等。
在下图中有用红色标记的节点,这个可以理解为count不为0的节点,即到该节点为止,可以形成一个单词。
因此下图中也包含b、abc、abd这些单词。
上面基本是对trie树的一个整体介绍。
2、trie树的操作对trie树的操作主要包括插入、查找等操作。
对trie树插入一个字符串,其实就是遍历该字符串,然后将每一位分别插入到相应的位置。
前缀树与后缀树了解前缀树和后缀树的应用与实现前缀树与后缀树:了解前缀树和后缀树的应用与实现在计算机科学领域中,有两种重要的数据结构,即前缀树(Trie树)和后缀树,它们被广泛应用于字符串处理、搜索引擎和自然语言处理等领域。
本文将介绍前缀树和后缀树的定义、应用以及实现方式。
一、前缀树前缀树,又称为Trie树,是一种特殊的多叉树,用于存储和快速检索字符串数据集。
前缀树的每个节点代表一个字符,从根节点到叶节点的路径构成一个完整的字符串。
每个节点包含指向子节点的指针,并用于在树中快速确定特定字符串的存在。
前缀树的一个主要应用是前缀匹配,即根据前缀快速查找以该前缀开头的所有字符串。
这在自动补全、拼写检查和搜索引擎的关键字建议中起着重要作用。
前缀树的实现可以使用数组、链表或哈希表等不同的数据结构,根据实际情况选择最适合的方式。
二、后缀树后缀树是一种特殊的树型数据结构,用于处理字符串集合中的后缀匹配问题。
与前缀树不同的是,后缀树是输入字符串的后缀的一种压缩表示方式。
通过构建后缀树,可以快速地确定一个字符串在字符串集合中的出现次数、最长公共子串等信息。
后缀树的应用非常广泛,比如字符串匹配、模式搜索和基因组序列分析等。
其高效的存储和查询性能使得后缀树成为处理大规模文本的理想解决方案。
后缀树的构建算法较为复杂,主要有朴素算法和Ukkonen算法等。
通过合理选择算法和数据结构,可以在合理的时间和空间复杂度内构建高效的后缀树。
三、前缀树与后缀树的应用与实现1. 字符串搜索与匹配:前缀树和后缀树可以用于快速确定一个字符串是否存在于给定的字符串集合中,并且可以高效地进行模式匹配和搜索操作。
2. 自动补全和拼写检查:通过构建前缀树,可以实现自动补全和拼写纠错功能,提升用户体验。
例如,当用户输入部分关键字时,前缀树可以快速返回与该前缀相关的所有可能的完整词语。
3. 文本处理和搜索引擎:后缀树在搜索引擎中扮演着重要角色,能够快速检索出包含特定关键字的文档。
Trie树算法算法介绍第⼀眼看到Trie树算法,⾸先明⽩的就是他⼀定是⽤树形结构实现的算法。
后来实现完整个算法才知道其实他也是压缩树,类似于哈弗曼编码和CF-Tree,因为树中保留了公共的前缀,减少了不必要的重复存储空间。
所以查询效率会⾼很多,如果你明⽩哈弗曼编码的实现过程,这个⾃然也是⼀样的道理。
那Trie树与Huffman编码树有什么区别呢,Huffman是0或1的编码,⽽Trie则是⽂本查找树,节点上可以是⼀个字母字符,也可以是汉字等等,⼤体就是这个意思。
好,下⾯说说算法的原理。
算法原理1、⾸先获取所有的⽂本数据,划分成逐条逐条的形式。
2、读⼊每⾏数据,对照当前⽐较字符值与当前节点的⼦节点⽐较,寻找到与之匹配的节点3、如果找到对应的⼦节点,将⼦节点作为当前节点,并移除数据的此字符,继续步骤2。
4、如果未找到对应⼦节点,新建节点插⼊当前的节点中,并将新节点作为当前节点,继续步骤2。
5、操作的终⽌条件为数据中的字符已经全部移除⽐较完毕。
算法实现输⼊的字符数据Input.txt:abcbcdbcabccbbdabca树节点类TreeNode.java:package Trie;import java.util.ArrayList;/****** @author lyq***/public class TreeNode {//节点的值String value;//节点孩⼦节点ArrayList<TreeNode> childNodes;public TreeNode(String value) {this.value = value;this.childNodes = new ArrayList<TreeNode>();}public ArrayList<TreeNode> getChildNodes() {return childNodes;}public void setChildNodes(ArrayList<TreeNode> childNodes) {this.childNodes = childNodes;}}算法⼯具类TrieTool.java:package Trie;import java.io.BufferedReader;import java.io.File;import java.io.FileReader;import java.io.IOException;import java.util.ArrayList;/****** @author lyq***/public class TrieTool {// 测试数据⽂件地址private String filePath;// 原始数据private ArrayList<String[]> datas;public TrieTool(String filePath) {this.filePath = filePath;readDataFile();}/**** 从⽂件中读取数据*/private void readDataFile() {File file = new File(filePath);ArrayList<String[]> dataArray = new ArrayList<String[]>();try {BufferedReader in = new BufferedReader(new FileReader(file));String str;String[] tempArray;while ((str = in.readLine()) != null) {tempArray = new String[str.length()];for (int i = 0; i < str.length(); i++) {tempArray[i] = str.charAt(i) + "";}dataArray.add(tempArray);}in.close();} catch (IOException e) {e.getStackTrace();}datas = dataArray;}/**** 构造Trie树**** @return*/public TreeNode constructTrieTree() {TreeNode rootNode = new TreeNode(null);ArrayList<String> tempStr;for (String[] array : datas) {tempStr = new ArrayList<String>();for (String s : array) {tempStr.add(s);}// 逐个字符串的添加addStrToTree(rootNode, tempStr);}return rootNode;}/**** 添加字符串的内容到Trie树中**** @param node** @param strArray*/private void addStrToTree(TreeNode node, ArrayList<String> strArray) { boolean hasValue = false;TreeNode tempNode;TreeNode currentNode = null;// ⼦节点中遍历寻找与当前第⼀个字符对应的节点for (TreeNode childNode : node.childNodes) {if (childNode.value.equals(strArray.get(0))) {hasValue = true;currentNode = childNode;break;}}// 如果没有找到对应节点,则将此节点作为新的节点if (!hasValue) {// 遍历到了未曾存在的字符值的,则新键节点作为当前节点的⼦节点tempNode = new TreeNode(strArray.get(0));// node.childNodes.add(tempNode);insertNode(node.childNodes, tempNode);currentNode = tempNode;}strArray.remove(0);// 如果字符已经全部查找完毕,则跳出循环if (strArray.size() == 0) {return;} else {addStrToTree(currentNode, strArray);}}/**** 将新建的节点按照字母排序的顺序插⼊到孩⼦节点中**** @param childNodes** 孩⼦节点** @param node** 新键的待插⼊的节点*/private void insertNode(ArrayList<TreeNode> childNodes, TreeNode node) { String value = node.value;int insertIndex = 0;for (int i = 0; i < childNodes.size() - 1; i++) {if (childNodes.get(i)pareTo(value) <= 0&& childNodes.get(i + 1)pareTo(value) > 0) {insertIndex = i + 1;break;}}if (childNodes.size() == 0) {childNodes.add(node);} else if (childNodes.size() == 1) {// 只有1个的情况额外判断if (childNodes.get(0)pareTo(value) > 0) {childNodes.add(0, node);} else {childNodes.add(node);}} else {childNodes.add(insertIndex, node);}}}测试类Client.java:package Trie;/**** Trie树算法** @author lyq***/public class Client {public static void main(String[] args) {String filePath = "C:\\Users\\lyq\\Desktop\\icon\\input.txt";TrieTool tool = new TrieTool(filePath);tool.constructTrieTree();}}算法的最终构造的树的形状⼤致如下(由于时间关系,我就没有写在控制台输出的程序了):root|a b| |---|b b c| | |----|-----|c d a c d|a算法的遗漏点和可以改进的地⽅这⾥所说的遗漏点就是在插⼊节点的时候,需要按照字母的排序插⼊,这是为了使得查找更加的⾼效。
字典树(TrieTree)讲解与实现 字典树,⼜称单词查找树,,是⼀种,是⼀种哈希树的变种。
典型应⽤是⽤于统计,排序和保存⼤量的字符串(但不仅限于字符串),所以经常被搜索引擎系统⽤于⽂本词频统计。
它的优点是:利⽤字符串的公共前缀来节约,最⼤限度地减少⽆谓的字符串⽐较,查询效率⽐⾼。
字典树与字典很相似,当你要查⼀个单词是不是在字典树中,⾸先看单词的第⼀个字母是不是在字典的第⼀层,如果不在,说明字典树⾥没有该单词,如果在就在该字母的孩⼦节点⾥找是不是有单词的第⼆个字母,没有说明没有该单词,有的话⽤同样的⽅法继续查找.字典树不仅可以⽤来储存字母,也可以储存数字等其它数据。
TrieNode结构定义:const int MAX = 26;typedef struct TrieNode{char *data; //储存结点数据,随需求变化bool isWord; //判断此结点前的字符串是否是⼀个单词TrieNode *branchs[MAX];};TireTree结构定义:class TrieTree{public:TrieNode *root;void initTrieTree();TrieNode *createNode();int insert(const char* word);int search(const char* word);};TireTree实现:Trie的查找(最主要的操作):(1) 每次从根结点开始⼀次搜索;(2) 取得要查找关键词的第⼀个字母,并根据该字母选择对应的⼦树并转到该⼦树继续进⾏检索; (3) 在相应的⼦树上,取得要查找关键词的第⼆个字母,并进⼀步选择对应的⼦树进⾏检索。
(4) 迭代过程…… (5) 在某个结点处,关键词的所有字母已被取出,则读取附在该结点上的信息,即完成查找。
void TrieTree::initTrieTree(){root = NULL;}TrieNode *TrieTree::createNode(){TrieNode *node = (TrieNode*)malloc(sizeof(TrieNode));node->data = NULL;node->isWord = false;for(int i = 1; i < MAX; i++)node->branchs[i] = NULL;return node;}int TrieTree::insert(const char* word) {if (root == NULL)root = createNode();TrieNode *p = root;int k = 0;while(*word){/*确定Branch ID*/if (*word >= 'a' && *word <= 'z')k = *word - 'a';else if (*word >= 'A' && *word <= 'Z') k = *word - 'A';elsereturn 0;if(p->branchs[k] == NULL){p->branchs[k] = createNode();p->branchs[k]->data = (char *)&word; }word++;if(!*word)p->branchs[k]->isWord = true;p = p->branchs[k];}// delete p;return 1;}int TrieTree::search(const char* word) {if(root == NULL)return 0;TrieNode *p = root;int k = 0;while(*word){/*确定Branch ID*/if (*word >= 'a' && *word <= 'z')k = *word - 'a';else if (*word >= 'A' && *word <= 'Z') k = *word - 'A';elsereturn 0;if(p->branchs[k] == NULL)return 0;word++;if(!*word && p->branchs[k]->isWord) return 1;p = p->branchs[k];}return 0;}测试代码:int main(int argc, char const *argv[]){TrieTree t;t.insert("ac");t.insert("abacus");t.insert("abalone");t.insert("abandon");t.insert("abandoned");t.insert("abashed");t.insert("abate");t.insert("this");if (t.search("ac"))cout<<"'ac' was found. pos: "<<endl;if (t.search("this"))cout<<"'this' was found. pos: "<<endl;if (t.search("abat"))cout<<"'abat' is found. pos: "<<endl;if (t.search("baby"))if (t.search("abacus"))cout<<"'abacus' is found. pos: "<<endl;if (t.search("baby"))cout<<"'baby' is found. pos: "<<endl;elsecout<<"'baby' does not exist at all!"<<endl; if (t.search("ac1"))cout<<"'ac1 was found. pos: "<<endl;return 0;}运⾏结果:'ac' was found. pos:'this' was found. pos:'baby' does not exist at all!。
字典树查找原理字典树(Trie树),又称前缀树或键树,是一种用于高效存储和查找字符串的数据结构。
它的查找原理基于字符串的公共前缀,可以在O(m)的时间复杂度内查找到字符串,其中m是字符串的长度。
字典树的基本结构是一个有根的树,每个节点包含若干个指向子节点的指针。
通常,一个节点对应一个字符,从根节点开始,根据字符串的每个字符依次向下遍历,直到到达字符串的最后一个字符或无法继续向下遍历为止。
字典树的插入操作是将字符串的每个字符依次插入到树中。
如果字符对应的子节点不存在,则创建一个新的节点,并将该节点与父节点连接。
重复这个过程,直到插入完整个字符串。
最后,将最后一个字符的节点标记为字符串的结束。
字典树的查找操作是从根节点开始,依次比较每个字符,根据字符找到对应的子节点,直到到达字符串的最后一个字符或无法继续向下遍历为止。
如果到达字符串的最后一个字符且该节点被标记为字符串的结束,说明字符串存在于字典树中;否则,字符串不存在。
字典树的优点是能够高效地存储和查找字符串。
它利用了字符串的公共前缀,可以大大减少存储空间和查找时间。
在字典树中,相同前缀的字符串共享相同的节点,因此可以节省存储空间。
而查找操作只需要依次比较每个字符,时间复杂度为O(m),其中m是字符串的长度,与字典树中字符串的数量无关。
字典树还可以用于前缀匹配。
通过查找某个前缀节点的子节点,可以找到以该前缀开头的所有字符串。
这在自动补全、搜索引擎和拼写检查等应用中非常有用。
然而,字典树的缺点是消耗大量的内存。
由于每个节点都需要存储指向子节点的指针,如果字符串的字符集很大,那么字典树将变得非常庞大。
此外,在构建字典树的过程中,需要频繁地动态分配内存,这可能导致内存碎片化。
为了解决字典树的内存消耗问题,可以使用压缩字典树(压缩前缀树)。
压缩字典树将只有一个子节点的节点进行合并,从而减少了存储空间。
同时,压缩字典树的查找操作也会稍微复杂一些,需要遍历字符串的每个字符,并判断是否需要跳过合并的节点。
二进制trie树算法二进制Trie树算法一、引言Trie树,又称前缀树或字典树,是一种用于高效存储和检索字符串的数据结构。
而二进制Trie树是一种特殊的Trie树,其节点的子节点仅包含两种可能的值:0和1。
本文将详细介绍二进制Trie树的原理、构建和应用。
二、原理二进制Trie树的原理基于二进制编码。
在二进制Trie树中,每个节点表示一个二进制位。
根节点表示最高位,而叶子节点表示最低位。
对于一个包含n个二进制位的关键字,需要构造一棵深度为n 的二进制Trie树。
三、构建方法1. 插入操作:将一个关键字插入到二进制Trie树中,需要从根节点开始,根据关键字的二进制位逐级向下寻找合适的位置。
如果当前节点为空,则创建一个新的节点;如果当前节点已存在,则继续向下寻找。
直到关键字的所有位都被插入完毕,将最后一个节点标记为叶子节点,表示该关键字已插入成功。
2. 搜索操作:在二进制Trie树中搜索一个关键字,同样需要从根节点开始,根据关键字的二进制位逐级向下寻找。
如果当前节点为空,则表示关键字不存在;如果当前节点为叶子节点,则表示关键字存在。
根据需求,还可以通过搜索操作来统计某个前缀出现的次数或查找具有某个前缀的关键字。
四、应用场景1. IP地址的匹配:在路由表中,使用二进制Trie树可以高效地匹配IP地址,确定数据包的转发路径。
2. 单词搜索:在字典中,使用二进制Trie树可以快速搜索指定前缀的单词,实现自动补全和相关推荐功能。
3. 文件压缩:在文件压缩算法中,使用二进制Trie树可以实现前缀编码,将常用的字符串编码为较短的二进制串,从而实现更高效的压缩效果。
4. 数据流量统计:在网络监控系统中,使用二进制Trie树可以实时统计不同前缀的数据流量,帮助管理员了解网络使用情况。
五、优缺点分析1. 优点:- 高效的存储和检索:二进制Trie树可以在O(n)的时间复杂度内完成插入和搜索操作,具有较快的速度。
- 灵活的应用场景:二进制Trie树可以用于各种字符串匹配和搜索问题,具有广泛的应用前景。
[转]Trie树2007-03-12 17:46转自发现导论上好像没有...收下了^ ^Trie树既可用于一般的字典搜索,也可用于索引查找。
对于给定的一个字符串a1,a2,a3,...,an.则采用TRIE树搜索经过n次搜索即可完成一次查找。
不过好像还是没有B树的搜索效率高,B树搜索算法复杂度为logt(n+1/2).当t趋向大,搜索效率变得高效。
怪不得DB2的访问内存设置为虚拟内存的一个PAGE大小,而且帧切换频率降低,无需经常的PAGE切换。
// trie.cpp : 定义控制台应用程序的入口点。
//#include"stdafx.h"#include<stdio.h>#include<iostream>//#include <ciostream.h>#include<string.h>using namespace std;const int num_chars = 26;class Trie {public:Trie();Trie(Trie& tr);virtual~Trie();int trie_search(const char* word,char* entry )const;int insert(const char* word,const char* entry);int remove(const char* word,char* entry);protected:struct Trie_node{char* data;Trie_node* branch[num_chars];Trie_node();};Trie_node* root;};Trie::Trie_node::Trie_node(){data =NULL;for(int i=0; i<num_chars;++i)branch[i]=NULL;}Trie::Trie():root(NULL){}Trie::~Trie(){}int Trie::trie_search(const char* word,char* entry )const {int position = 0;char char_code;Trie_node *location = root;while( location!=NULL &&*word!=0 ){if(*word>='A'&&*word<='Z')char_code =*word-'A';else if(*word>='a'&&*word<='z')char_code =*word-'a';else return 0;location = location->branch[char_code];position++;word++;}if( location !=NULL&& location->data !=NULL){strcpy(entry,location->data);return 1;}else return 0;}int Trie::insert(const char* word,const char* entry){int result = 1, position = 0;if( root ==NULL) root =new Trie_node;char char_code;Trie_node *location = root;while( location!=NULL &&*word!=0 ){if(*word>='A'&&*word<='Z')char_code =*word-'A';else if(*word>='a'&&*word<='z')char_code =*word-'a';else return 0;if( location->branch[char_code]==NULL)location->branch[char_code]=new Trie_node;location = location->branch[char_code];position++;word++;}if(location->data !=NULL)result = 0;else{location->data =new char[strlen(entry)+1];strcpy(location->data, entry);}return result;}int main(){Trie t;char entry[100];t.insert("aa","DET");t.insert("abacus","NOUN");t.insert("abalone","NOUN");t.insert("abandon","VERB");t.insert("abandoned","ADJ");t.insert("abashed","ADJ");t.insert("abate","VERB");t.insert("this","PRON");if(t.trie_search("this", entry))cout<<"'this' was found. pos: "<<entry<<endl;if(t.trie_search("abate", entry))cout<<"'abate' is found. pos: "<<entry<<endl;if(t.trie_search("baby", entry))10.3 Trie树当关键码是可变长时,Trie树是一种特别有用的索引结构。
10.3.1 Trie树的定义Trie树是一棵度m ≥ 2 的树,它的每一层分支不是靠整个关键码的值来确定,而是由关键码的一个分量来确定。
如下图所示Trie树,关键码由英文字母组成。
它包括两类结点:元素结点和分支结点。
元素结点包含整个key数据;分支结点有27个指针,其中有一个空白字符‘b’,用来终结关键码;其它用来标识‘a’, ‘b’,..., ‘z’等26个英文字母。
在第0层,所有的关键码根据它们第0位字符, 被划分到互不相交的27个类中。
因此,root→brch.link[i] 指向一棵子Trie树,该子Trie树上所包含的所有关键码都是以第 i 个英文字母开头。
若某一关键码第 j 位字母在英文字母表中顺序为 i ( i = 0, 1, ?, 26 ), 则它在Trie树的第 j 层分支结点中从第 i 个指针向下找第 j+1 位字母所在结点。
当一棵子Trie树上只有一个关键码时,就由一个元素结点来代替。
在这个结点中包含有关键码,以及其它相关的信息,如对应数据对象的存放地址等。
Trie树的类定义:const int MaxKeySize = 25; //关键码最大位数typedef struct { //关键码类型char ch[MaxKeySize]; //关键码存放数组int currentSize; //关键码当前位数} KeyType;class TrieNode { //Trie树结点类定义friend class Trie;protected:enum { branch, element } NodeType; //结点类型union NodeType { //根据结点类型的两种结构struct { //分支结点int num; //本结点关键码个数TrieNode *link[27]; //指针数组} brch;struct { //元素结点KeyType key; //关键码recordNode *recptr; //指向数据对象指针} elem;}}class Trie { //Trie树的类定义private:TrieNode *root, *current;public:RecordNode* Search ( const keyType & );int Insert ( const KeyType & );int Delete ( const KeyType & );}10.3.2 Trie树的搜索为了在Trie树上进行搜索,要求把关键码分解成一些字符元素, 并根据这些字符向下进行分支。
函数 Search 设定 current = NULL, 表示不指示任何一个分支结点, 如果current 指向一个元素结点 elem,则 curr ent→elem.key 是 current 所指结点中的关键码。
Trie树的搜索算法:RecordNode* Trie::Search ( const KeyType & x ) {k = x.key;concatenate ( k, ‘b’ );current = root;int i = 0; //扫描初始化while( current != NULL && current→NodeType != element && i <= x.ch[i] ) {current = current→brch.link[ord (x.ch[i])];i = i++;};if( current != NULL && current→NodeType == element &¤t→elem.key == x )return current→recptr;elsereturn NULL;}经验证,Trie树的搜索算法在最坏情况下搜索的时间代价是 O(l)。