Trie树的用途很多,可以实现根据前缀补全等功能。
在一个国外的博客上看到这个Java实现,比较简洁,个人修改了一下,增加了根据前缀删除和遍历的功能。
Trie树节点类定义:
import java.util.*;
/**
* A Trie Node
*/
class TrieNode {
/**
* The key stored in this node if any.
*/
private String key;
/**
* the outgoing edges of this node, implemented as a sorted map of character to the child node.
*/
private SortedMap<Character, TrieNode> edges;
TrieNode addEdge(char c) {
if (edges == null) {
edges = new TreeMap<Character, TrieNode>();
}
TrieNode childNode = new TrieNode();
edges.put(c, childNode);
return childNode;
}
TrieNode getNodeByEdge(char c) {
return (edges == null) ? null : edges.get(c);
}
TrieNode deleteEdge(char c) {
return (edges == null) ? null : edges.remove(c);
}
Iterator<TrieNode> getChildren() {
return (edges == null) ? null : edges.values().iterator();
}
void setKey(String key) {
this.key = key;
}
String getKey() {
return key;
}
public int getChildrenCnt() {
return edges == null ? 0 : edges.size();
}
}
Trie树的操作:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* TRIE data structure supporting basic dictionary operations.
*/
public class Trie {
private final TrieNode root;
/**
* Creates a new empty TRIE object.
*/
public Trie() {
root = new TrieNode();
}
/**
* Inserts the specified key into this Trie object.
* @param key
*/
public void insert(String key) {
TrieNode currNode = root;
for (char c : key.toCharArray()) {
TrieNode child = currNode.getNodeByEdge(c);
if (child == null) {
currNode = currNode.addEdge(c);
} else {
currNode = child;
}
}
currNode.setKey(key);
}
/**
* Returns all the keys in the Trie which start with the specified prefix.
* @param prefix
* @return
*/
public List<String> searchPrefix(String prefix) {
TrieNode currNode = root;
for (char c : prefix.toCharArray()) {
TrieNode child = currNode.getNodeByEdge(c);
if (child == null) {
return Collections.emptyList();
} else {
currNode = child;
}
}
List<String> matches = new ArrayList<String>();
preorderTraverse(currNode, matches);
return matches;
}
/**
* Delete all children below the specified prefix.
* @param prefix
* @return true if successful; false if prefix not found.
*/
public boolean deletePrefix(String prefix) {
TrieNode currNode = root;
TrieNode prevNode = root;
char delFrom = ' ';
for (char c : prefix.toCharArray()) {
TrieNode child = currNode.getNodeByEdge(c);
delFrom = c;
if (child == null) {
return false;
} else {
prevNode = currNode;
currNode = child;
}
}
prevNode.deleteEdge(delFrom);
return true;
}
/**
* Does preorder traversal of Trie and add retrieved keys in the specified results list.
* @param currNode
* @param results
*/
private void preorderTraverse(TrieNode currNode, List<String> results) {
if (currNode.getKey() != null) {
results.add(currNode.getKey());
}
Iterator<TrieNode> children = currNode.getChildren();
if (children != null) {
while (children.hasNext()) {
preorderTraverse(children.next(), results);
}
}
}
/**
* Does preorder traversal of Trie and print all keys.
* @param currNode
* @param results
*/
public void traverse() {
preorderTraverse(root);
}
private void preorderTraverse(TrieNode root) {
if (root.getKey() != null) {
System.out.println(root.getKey());
}
Iterator<TrieNode> children = root.getChildren();
if (children != null) {
while (children.hasNext()) {
preorderTraverse(children.next());
}
}
}
public static void main(String[] args) {
Trie t = new Trie();
t.insert("vino");
t.insert("vinod");
t.insert("vin");
t.insert("jyo");
t.insert("jyotsna");
t.insert("jyot");
t.insert("jyots");
t.insert("jyotsn");
t.insert("joe");
System.out.println(t.searchPrefix("vin"));
System.out.println(t.searchPrefix("j"));
System.out.println(t.searchPrefix("jy"));
System.out.println(t.searchPrefix("joe"));
System.out.println(t.searchPrefix("bhalblah"));
t.deletePrefix("vino");
System.out.println(t.searchPrefix("v"));
t.traverse();
}
}