简介
前缀匹配大家都不陌生,运用也很广泛,常见的应用是输入框的自动提示,像Linux这样的操作系统命令行的自动补全,也是前缀匹配的一个应用。
字典树
字典树有如下性质
1,根节点不包含字符,除根节点意外每个节点只包含一个字符。
2,从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。
3,每个节点的所有子节点包含的字符串不相同。
代码实现
字典树节点的定义
private static class Node {
Node(char c) {
this.ch = c;
}
boolean wordEnd = false; /*是否是某个单词的最后一个字母*/
char ch; /*这个节点所代表的字母*/
Map<Character, Node> children = new HashMap<>(); /*字典树是哈系树的一种,子节点可以用HashMap来存储*/
}
字典树的构建
private final Node root = new Node('\0'); /*需要一个根节点*/
public void insert(String word) {
if (word.isEmpty()) {
return;
}
char[] arr = word.toCharArray();
Node cur = root;
for (char c : arr) {
if (!cur.children.containsKey(c)) {
cur.children.put(c, new Node(c));
}
cur = cur.children.get(c);
}
cur.wordEnd = true;
}
字典树的查找
public Set<String> search(String prefix) {
char[] arr = prefix.toCharArray();
Node cur = root;
Set<String> resultSet = new HashSet<>();
for (char c : arr) {
cur = cur.children.get(c);
if (cur == null)
break;
}
if (cur != null) {
getAllWords(cur, prefix, resultSet);
}
return resultSet;
}
private void getAllWords(Node node, String prefix, Set<String> resultSet) {
if (node.wordEnd) {
resultSet.add(prefix);
}
for (Map.Entry<Character, Node> e : node.children.entrySet()) {
getAllWords(e.getValue(), prefix + e.getKey(), resultSet);
}
}
完整代码
public class TireTree {
private final Node root = new Node('\0');
public void insert(String word) {
if (word.isEmpty()) {
return;
}
char[] arr = word.toCharArray();
Node cur = root;
for (char c : arr) {
if (!cur.children.containsKey(c)) {
cur.children.put(c, new Node(c));
}
cur = cur.children.get(c);
}
cur.wordEnd = true;
}
public Set<String> search(String prefix) {
char[] arr = prefix.toCharArray();
Node cur = root;
Set<String> resultSet = new HashSet<>();
for (char c : arr) {
cur = cur.children.get(c);
if (cur == null)
break;
}
if (cur != null) {
getAllWords(cur, prefix, resultSet);
}
return resultSet;
}
private void getAllWords(Node node, String prefix, Set<String> resultSet) {
if (node.wordEnd) {
resultSet.add(prefix);
}
for (Map.Entry<Character, Node> e : node.children.entrySet()) {
getAllWords(e.getValue(), prefix + e.getKey(), resultSet);
}
}
private static class Node {
Node(char c) {
this.ch = c;
}
boolean wordEnd = false; /*是否是某个单词的最后一个字母*/
char ch; /*这个节点所代表的字母*/
Map<Character, Node> children = new HashMap<>();
}
}
客户端调用
public class Client {
private static String[] words = new String[]{
"assassin",
"arc",
"archer",
"abandon",
"angel",
...
/* 中间省略一些 */
...
"yummy",
"zero",
"zone",
"zoom"
};
public static void main(String[] args) {
TireTree tireTree = new TireTree();
for (String word : words) {
tireTree.insert(word);
}
long start, end;
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
String word = sc.next();
start = System.currentTimeMillis();
System.out.printf("Result set = %s\n", tireTree.search(word));
end = System.currentTimeMillis();
System.out.println("Search cost: "+(end - start) + "ms");
}
}
}
运行结果
>> app
Result set = [apple, apply]
Search cost: 8ms
>> un
Result set = [unity, unit, until, unix]
Search cost: 0ms
>> ca
Result set = [canada, car, candy, cat]
Search cost: 0ms
>> protect
Result set = [protected, protection, protect]
Search cost: 0ms
这里我还做了一个JS在线Demo的演示,实现的是在输入框输入前缀自动提示