一、概述
1.1 基本概念
字典树,又称为单词查找树,Tire数,是一种树形结构,它是一种哈希树的变种。
1.2 基本性质
- 根节点不包含字符,除根节点外的每一个子节点都包含一个字符
- 从根节点到某一节点。路径上经过的字符连接起来,就是该节点对应的字符串
- 每个节点的所有子节点包含的字符都不相同
1.3 应用场景
典型应用是用于统计,排序和保存大量的字符串(不仅限于字符串),经常被搜索引擎系统用于文本词频统计。
1.4 优点
利用字符串的公共前缀来减少查询时间,最大限度的减少无谓的字符串比较,查询效率比哈希树高。
二、trie树的构建与操作实现
2.1 字典树节点定义
//定义字典表节点(简化设计,仅兼容26个小写英文字符)
class TrieNode {private int num; //有多少单词经过当前节点,即由根节点到该节点组成的字符串模式出现的次数private TrieNode[] son;//子节点数组private boolean wordEnd;//是否是叶子结点private char val;//当前节点的值TrieNode() {num =1;son = new TrieNode[engNum];wordEnd = false;}
}
2.2 字典树构造函数
public Trie() {root = new TrieNode();}
2.3 建立字典树
//建立字典树
public void insert(String str) {if(StringUtils.isEmpty(str)) {return;}TrieNode curNode = root;char[] arr = str.toCharArray();for(char c:arr) {int index = c-'a';TrieNode node = curNode.son[index];if(node == null) {//当前节点为空则创建curNode.son[index] = new TrieNode();curNode.son[index].val = c;}else {//不为空则加一node.num = node.num +1;}curNode = curNode.son[index];}curNode.wordEnd = true;
}
2.4 在字典树中查找是否完全匹配一个指定的字符串
//在字典中查找一个完全匹配的单词
public boolean has(String str) {if(StringUtils.isEmpty(str)) {return false;}TrieNode node = root;//找到指定前缀在字典树的位置char[] arr = str.toCharArray();for(int i=0;i<arr.length;i++) {int index = arr[i] -'a';if(node.son[index] == null) {return false;}node = node.son[index];}//到这里,如果最后的字符是叶子结点,则完全匹配;否则为部分匹配;return node.wordEnd;
}
2.5 前序遍历字典树
//前序遍历字典树
public void preTraverse(TrieNode root) {if(root != null) {System.out.println(root.val);for(TrieNode son: root.son) {preTraverse(son);}}
}
2.6 计算指定前缀的单词的数量
//计算指定前缀的单词的数量
public int countPrefix(String prefix) {if(StringUtils.isEmpty(prefix)) {return 0;}TrieNode node = root;char[] arr = prefix.toCharArray();for(int i=0;i<arr.length;i++) {int index = arr[i] -'a';if(node.son[index] == null) {return 0;}node = node.son[index];}return node.num;
}
2.7 完整代码
public class Trie {public static int asciiNum = 128;public static int engNum = 26;private TrieNode root;public Trie() {root = new TrieNode();}//定义字典表节点(简化设计,仅兼容26个小写英文字符)class TrieNode {private int num; //有多少单词经过当前节点,即由根节点到该节点组成的字符串模式出现的次数private TrieNode[] son;//子节点数组private boolean wordEnd;//是否是叶子结点private char val;//当前节点的值TrieNode() {num =1;son = new TrieNode[engNum];wordEnd = false;}}//建立字典树public void insert(String str) {if(StringUtils.isEmpty(str)) {return;}TrieNode curNode = root;char[] arr = str.toCharArray();for(char c:arr) {int index = c-'a';TrieNode node = curNode.son[index];if(node == null) {//当前节点为空则创建curNode.son[index] = new TrieNode();curNode.son[index].val = c;}else {//不为空则加一node.num = node.num +1;}curNode = curNode.son[index];}curNode.wordEnd = true;}//计算指定前缀的单词的数量public int countPrefix(String prefix) {if(StringUtils.isEmpty(prefix)) {return 0;}TrieNode node = root;char[] arr = prefix.toCharArray();for(int i=0;i<arr.length;i++) {int index = arr[i] -'a';if(node.son[index] == null) {return 0;}node = node.son[index];}return node.num;}//打印指定前缀的单词public void hasPrefix(String prefix) {if(StringUtils.isEmpty(prefix)) {return;}TrieNode node = root;//找到指定前缀在字典树的位置char[] arr = prefix.toCharArray();for(int i=0;i<arr.length;i++) {int index = arr[i] -'a';if(node.son[index] == null) {return;}node = node.son[index];}//前序遍历字典树preTraverse(node, prefix);}private void preTraverse(TrieNode node, String prefix) {if(node.wordEnd) {System.out.println(prefix);}for(TrieNode son : node.son) {if(son != null) {preTraverse(son, prefix + son.val);}}}//在字典中查找一个完全匹配的单词public boolean has(String str) {if(StringUtils.isEmpty(str)) {return false;}TrieNode node = root;//找到指定前缀在字典树的位置char[] arr = str.toCharArray();for(int i=0;i<arr.length;i++) {int index = arr[i] -'a';if(node.son[index] == null) {return false;}node = node.son[index];}//到这里,如果最后的字符是叶子结点,则完全匹配;否则为部分匹配;return node.wordEnd;}//前序遍历字典树public void preTraverse(TrieNode root) {if(root != null) {System.out.println(root.val);for(TrieNode son: root.son) {preTraverse(son);}}}public static void main(String[] args) {Trie trie = new Trie();String [] arr = new String[]{"string","str","study","strong","tech", "teach","teacher"};trie.insert("");for(String str: arr) { trie.insert(str);}
// trie.preTraverse(trie.root);String prefix ="tea";System.out.println(trie.countPrefix(prefix));trie.hasPrefix(prefix);String word = "teach";System.out.println(trie.has(word));}}