一、字典樹簡介
Trie也叫做字典樹、前綴樹、單詞查找樹。
Trie搜索字符串的效率主要跟字符串的長度有關。
前綴搜索的時候,用Trie就是典型的空間換時間,差不多一個字符需要一個結點來存儲。
二、實現
1、寫代碼注意點
怎麼維護每個結點的多個兒子?
用HashMap維護兒子結點,每個字符對應一個結點即可。
怎麼判斷Trie中是否包含某個字符串?
每個結點中設置一個標記位即可,當是字符串結尾的時候,設置標記位,否則就是默認的。
怎麼刪除結點?
先判斷要刪除的字符串在不在字典樹中,如果在的話,再判斷要刪除的結點是否有兒子,如果有兒子直接將標記位重置成默認的即可。沒有兒子的話,從字符串的最後個字符開始從對應父結點的HashMap中刪除,如果刪除後父結點有孩子或者標記位不是默認值,跳出循環。
2、代碼
public class Trie<V> {
private int size = 0;
Node<V> root;
private static class Node<V> {
Node<V> parent;
HashMap<Character, Node<V>> children;
boolean isWord;
V value;
public Node(Node<V> parent) {
this.parent = parent;
}
}
public Trie() {
}
public int size() {
return this.size;
}
public boolean isEmpty() {
return this.size == 0;
}
public void clear() {
size = 0;
root = null;
}
/**
* 判斷Trie中是否包含某個字符串
*
* @param key
* @return
*/
public boolean contains(String key) {
return get(key) == null ? false : true;
}
/**
* 判斷Trie中是否包含以某個字符串爲前綴的字符串
*
* @param prefix
* @return
*/
public boolean startsWith(String prefix) {
Node<V> node = root;
for (int i = 0; i < prefix.length(); i++) {
Character c = prefix.charAt(i);
node = node.children.get(c);
if (node == null)
return false;
}
return true;
}
/**
* 添加一對元素, 如果key已存在就用新的value覆蓋老的value
*
* @param str
* @param value
* @return
*/
public V add(String str, V value) {
V old = null;
if (root == null)
root = new Node<V>(null);
Node<V> node = root;
for (int i = 0; i < str.length(); i++) {
Character c = str.charAt(i);
if (node.children == null)
node.children = new HashMap<>();
Node<V> child = node.children.get(c);
if (child != null) { //兒子結點已存在就繼續遍歷孫子結點
old = node.children.get(c).value;
node = child;
} else { //兒子不存在就new結點
Node<V> newNode = new Node<V>(node);
node.children.put(c, newNode);
node = newNode;
}
}
node.isWord = true;
node.value = value;
size++;
return old;
}
public V remove(String str) {
if (!contains(str))
throw new IllegalArgumentException("被刪除的字符串不存在!");
V old = null;
Node<V> node = getNode(str);
if (node.children != null) { //被刪除的結點還有兒子結點直接刪除value和重置爲默認標記位
node.isWord = false;
old = node.value;
node.value = null;
size--;
return old;
}
for (int i = str.length() - 1; i >= 0; i--) {
Character c = str.charAt(i);
System.out.println(c);
if (i == str.length() - 1) {
old = node.value;
node.value = null; //清空標記位上的value
}
node = node.parent;
node.children.remove(c);
if (node.children.size() != 0 || node.isWord)
break;
}
size--;
return old;
}
public V get(String key) {
Node<V> node = getNode(key);
return node == null ? null : node.value;
}
private Node<V> getNode(String key) {
Node<V> node = root;
for (int i = 0; i < key.length(); i++) {
Character c = key.charAt(i);
if (node.children == null)
return null;
node = node.children.get(c);
if (node == null)
return null;
}
if (node.isWord)
return node;
else
return null;
}
}