import java.util.LinkedList;
import java.util.Queue;
/**
* R-way implementation of Trie
* <p>
* API list
* 1. void put(String key, T val)
* 2. T get(String key)
* 3. delete(String key)
* 4. boolean contains(String key)
* 5. boolean isEmpty()
* 6. String longestPrefixOf(String s)
* 7. Iterable<String> keysWithPrefix(String s)
* 8. Iterable<String> keysThatMatch(String s)
* 9. int size()
* 10. Iterable<String> keys()
* <p>
* no duplicate or null keys
* no null values
*/
public class TrieST<T> {
/**
* radix
*/
private static int R = 256;
/**
* root of trie
*/
private Node root = new Node();
/**
* get the value from trie with the key
*
* @return the value of the key. otherwise, return null
*/
public T get(String key) {
Node n = get(root, key, 0);
if (n == null) {
return null;
}
return (T) n.val;
}
private Node get(Node n, String key, int d) {
// Return node associated with key in the subtrie rooted at n.
if (n == null) {
return null;
}
if (d == key.length()) {
return n;
}
char c = key.charAt(d); // Use dth key char to identify subtrie.
return get(n.next[c], key, d + 1);
}
/**
* insert an entry to the trie
*/
public void put(String key, T val) {
root = put(root, key, val, 0);
}
private Node put(Node n, String key, T val, int d) {
// Change value associated with key if in subtrie rooted at n.
if (n == null) {
n = new Node();
}
if (d == key.length()) {
n.val = val;
return n;
}
char c = key.charAt(d); // Use dth key char to identify subtrie.
n.next[c] = put(n.next[c], key, val, d + 1);
return n;
}
/**
* 1. maintain the number of keys in an instance variable N
* 2. maintain the number of keys in a subtrie as a node instance variable, updated after the recursive calls in
* put() and delete()
* 3. calculate recursively
*
* @return number of keys in the trie
*/
public int size() {
return size(root);
}
private int size(Node n) {
// lazy recursively implementation
// traverse all of the nodes in the trie
// counting the number having a non-null value
// bad performance
if (n == null) {
return 0;
}
int cnt = 0;
if (n.val != null) {
cnt++; // add its own size
}
for (char c = 0; c < R; c++) {
cnt += size(n.next[c]); // add children's size
}
return cnt;
}
public Iterable<String> keys() {
return keysWithPrefix("");
}
public Iterable<String> keysWithPrefix(String pre) {
Queue<String> q = new LinkedList<>();
collect(get(root, pre, 0), pre, q);
return q;
}
private void collect(Node n, String pre, Queue<String> q) {
if (n == null) {
return;
}
if (n.val != null) {
q.offer(pre); // pre is a valid key
}
for (char c = 0; c < R; c++) {
collect(n.next[c], pre + c, q); // collect keys in children
}
}
/**
* support '.' only
*
* @param pat string pattern with or without '.'
* @return all keys that match the given pattern
*/
public Iterable<String> keysThatMatch(String pat) {
Queue<String> q = new LinkedList<>();
collect(root, "", pat, q);
return q;
}
private void collect(Node n, String pre, String pat, Queue<String> q) {
int d = pre.length();
if (n == null) {
return;
}
if (d == pat.length() && n.val != null) {
q.offer(pre);
}
if (d == pat.length()) {
return;
}
char next = pat.charAt(d);
for (char c = 0; c < R; c++) {
if (next == '.' || next == c) {
collect(n.next[c], pre + c, pat, q);
}
}
}
/**
* @return the longest prefix exists in the trie
*/
public String longestPrefix(String s) {
int length = search(root, s, 0, 0);
return s.substring(0, length);
}
private int search(Node n, String s, int d, int length) {
if (n == null) {
return length;
}
if (n.val != null) {
length = d; // find a key, update length
}
if (d == s.length()) {
return length; // reach the deepest level
}
char c = s.charAt(d);
return search(n.next[c], s, d + 1, length); // search next level
}
/**
* delete a key and its corresponding value from the trie
*/
public void delete(String key) {
root = delete(root, key, 0);
}
private Node delete(Node n, String key, int d) {
if (n == null) {
return null;
}
if (d == key.length()) { // find the correct level
n.val = null;
} else {
char c = key.charAt(d); // recurse further to next level
n.next[c] = delete(n.next[c], key, d + 1);
}
// check if there is no more keys in the subtrie
if (n.val != null) {
return n; // check current node
}
for (char c = 0; c < R; c++) {
if (n.next[c] != null) {
return n; // check children
}
}
return null; // the client value and all of the links in this node are all
}
private static class Node {
private Object val;
private Node[] next = new Node[R];
}
}