需求
如何判断一堆不重复的字符串是否以某个前缀开头?
用Set\Map存储某个字符串 --- 遍历所有的字符串进行判断 --- 时间复杂度为O(n)
Trie
◼
Trie 也叫做字典树、前缀树(Prefix Tree)、单词查找树
◼
Trie 搜索字符串的效率主要跟字符串的长度有关
◼ 假设使用 Trie 存储 cat、dog、doggy、does、cast、add 六个单词
接口
代码
- Asserts
public class Asserts { public Asserts() { } public static void test(boolean value) { try { if (!value) { throw new Exception("测试未通过"); } } catch (Exception var2) { var2.printStackTrace(); } } }
- Trie
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.mj; import java.util.HashMap; public class Trie<V> { private int size; private Trie.Node<V> root; public Trie() { } public int size() { return this.size; } public boolean isEmpty() { return this.size == 0; } public void clear() { this.size = 0; this.root = null; } public V get(String key) { Trie.Node<V> node = this.node(key); return node != null && node.word ? node.value : null; } public boolean contains(String key) { Trie.Node<V> node = this.node(key); return node != null && node.word; } public V add(String key, V value) { this.keyCheck(key); if (this.root == null) { this.root = new Trie.Node((Trie.Node)null); } Trie.Node<V> node = this.root; int len = key.length(); for(int i = 0; i < len; ++i) { char c = key.charAt(i); boolean emptyChildren = node.children == null; Trie.Node<V> childNode = emptyChildren ? null : (Trie.Node)node.children.get(c); if (childNode == null) { childNode = new Trie.Node(node); childNode.character = c; node.children = emptyChildren ? new HashMap() : node.children; node.children.put(c, childNode); } node = childNode; } if (node.word) { V oldValue = node.value; node.value = value; return oldValue; } else { node.word = true; node.value = value; ++this.size; return null; } } public V remove(String key) { Trie.Node<V> node = this.node(key); if (node != null && node.word) { --this.size; V oldValue = node.value; if (node.children != null && !node.children.isEmpty()) { node.word = false; node.value = null; return oldValue; } else { for(Trie.Node parent = null; (parent = node.parent) != null; node = parent) { parent.children.remove(node.character); if (parent.word || !parent.children.isEmpty()) { break; } } return oldValue; } } else { return null; } } public boolean startsWith(String prefix) { return this.node(prefix) != null; } private Trie.Node<V> node(String key) { this.keyCheck(key); Trie.Node<V> node = this.root; int len = key.length(); for(int i = 0; i < len; ++i) { if (node == null || node.children == null || node.children.isEmpty()) { return null; } char c = key.charAt(i); node = (Trie.Node)node.children.get(c); } return node; } private void keyCheck(String key) { if (key == null || key.length() == 0) { throw new IllegalArgumentException("key must not be empty"); } } private static class Node<V> { Trie.Node<V> parent; HashMap<Character, Trie.Node<V>> children; Character character; V value; boolean word; public Node(Trie.Node<V> parent) { this.parent = parent; } } }
主函数测试
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.mj; public class Main { public Main() { } static void test1() { Trie<Integer> trie = new Trie(); trie.add("cat", 1); trie.add("dog", 2); trie.add("catalog", 3); trie.add("cast", 4); trie.add("小码哥", 5); Asserts.test(trie.size() == 5); Asserts.test(trie.startsWith("do")); Asserts.test(trie.startsWith("c")); Asserts.test(trie.startsWith("ca")); Asserts.test(trie.startsWith("cat")); Asserts.test(trie.startsWith("cata")); Asserts.test(!trie.startsWith("hehe")); Asserts.test((Integer)trie.get("小码哥") == 5); Asserts.test((Integer)trie.remove("cat") == 1); Asserts.test((Integer)trie.remove("catalog") == 3); Asserts.test((Integer)trie.remove("cast") == 4); Asserts.test(trie.size() == 2); Asserts.test(trie.startsWith("小")); Asserts.test(trie.startsWith("do")); Asserts.test(!trie.startsWith("c")); } public static void main(String[] args) { test1(); } }
总结