Trie中文名又叫做字典树,前缀树等,因为其结构独有的特点,经常被用来统计,排序,和保存大量的字符串,经常见于搜索提示,输入法文字关联等,当输入一个值,可以自动搜索出可能的选择。当没有完全匹配的结果时,可以返回前缀最为相似的可能。 例如:上面的树由abcd abc abd,b等构成,红点表示可以为某个单词的尾巴,最上面的空白就是root节点(不携带值的) 常见的api有insert(将一个单词插入),search(查找是否存在某个单词),startsWith(是否是以某个单词作为前缀开头) 下面简单实现,使用search的来返回结果来告知是否是前缀或者存在此单词
class Trie { class TNode { // 孩子节点,英文的话最多26个字母,此处可以用HashMap来代替,可以处理中文场景等 TNode[] ch = new TNode[26]; // 是否可以为某个单词的尾巴 boolean isEnd; // 当前字符内容,以下均是看使用场景,不是必须 char v; }
TNode root = new TNode(); void insert(String s) { // 每次插入都要从头开始构建trie树 TNode parent = root; char[] cs = s.toCharArray(); for (char c : cs) { if (parent.ch[c - 'a'] == null) { // 新的字符,则需要新建一个节点 parent.ch[c - 'a'] = new TNode(); parent.ch[c - 'a'].v = c; } parent = parent.ch[c - 'a']; } parent.isEnd = true; } // -1是不存在或者不存在前缀,0是存在前缀,1是存在此单词 int search(String s) { TNode parent = root; char[] cs = s.toCharArray(); for (char c : cs) { if (parent.ch[c - 'a'] == null) { // 说明不存在这个字符,也就是不存在这个单词 return -1; } // 指针指向child parent = parent.ch[c - 'a']; } return parent.isEnd ? 1 : 0; } }
接下来我们实现一个简单的输入法提示:
class Trie { class TNode { // 孩子节点,英文的话最多26个字母,此处可以用HashMap来代替,可以处理中文场景等 TNode[] ch = new TNode[26]; // 是否可以为某个单词的尾巴 boolean isEnd; // 当前字符内容,以下均是看使用场景,不是必须 char v; } TNode root = new TNode(); void insert(String s) { // 每次插入都要从头开始构建trie树 TNode parent = root; char[] cs = s.toCharArray(); for (char c : cs) { if (parent.ch[c - 'a'] == null) { // 新的字符,则需要新建一个节点 parent.ch[c - 'a'] = new TNode(); parent.ch[c - 'a'].v = c; } parent = parent.ch[c - 'a']; } parent.isEnd = true; } // -1是不存在或者不存在前缀,0是存在前缀,1是存在此单词 int search(String s) { TNode parent = root; char[] cs = s.toCharArray(); for (char c : cs) { if (parent.ch[c - 'a'] == null) { // 说明不存在这个字符,也就是不存在这个单词 return -1; } // 指针指向child parent = parent.ch[c - 'a']; } return parent.isEnd ? 1 : 0; } /** * 返回匹配出的搜索结果集合 */ List<String> input(String word) { TNode parent = root; char[] cs = word.toCharArray(); for (char c : cs) { if (parent.ch[c - 'a'] == null) { return Collections.emptyList(); } parent = parent.ch[c - 'a']; } // 现在位于最后一个字母就是parent.v List<String> res = new ArrayList<>(); LinkedList<Character> ss = new LinkedList<>(); // 找到所有的尾巴 dfs(parent, ss, res); // 将word(也就是头)和尾巴拼上构建最终的结果 List<String> res2 = new ArrayList<>(); for (String s : res) { res2.add(word + s); } return res2; } /** * 使用dfs回溯,将所有包含此前缀的单词都保存到res中 */ private void dfs(TNode parent, LinkedList<Character> ss, List<String> res) { if (parent.isEnd) { StringBuilder sb = new StringBuilder(); // 将字符构建成String for (Character c : ss) { sb.append(c); } res.add(sb.toString()); } for (int i = 0; i < parent.ch.length; i++) { if (parent.ch[i] != null) { ss.add(parent.ch[i].v); // 回溯 dfs(parent.ch[i], ss, res); ss.removeLast(); } } } }
注意上面的input就是我们实现的英文输入提示,他根据我们的输入匹配所以可能的结果,如下测试:
public static void main(String[] args) { Trie tree = new Trie(); String[] strs = {"app", "apple", "add"}; // 构建词库,就是构建字典树使用insert一个一个单词插入 for (String str : strs) { tree.insert(str); } System.out.println(tree.input("a"));// [add, app, apple] System.out.println(tree.input("ap"));// [app, apple] System.out.println(tree.input("app"));// [app, apple] System.out.println(tree.input("appl"));// [apple] System.out.println(tree.input("dd"));// [] }
字典树的应用非常广泛,自己动手实现一个我们每天都会接触的输入法的提示也是有点意思!
标签:单词,ch,前缀,parent,Trie,TNode,char,new,字典 From: https://www.cnblogs.com/junlancer/p/16724216.html