實現一個 Trie (前綴樹),包含 insert
, search
, 和 startsWith
這三個操作。
示例:
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple"); // 返回 true
trie.search("app"); // 返回 false
trie.startsWith("app"); // 返回 true
trie.insert("app");
trie.search("app"); // 返回 true
說明:
- 你可以假設所有的輸入都是由小寫字母
a-z
構成的。 - 保證所有輸入均爲非空字符串
第一種:R向單詞查找樹,每個節點都含有R個鏈接,對應着每個可能出現的字符,優點在於查找和插入的效率很高,與單詞的長度成正比。缺點是空間消耗大。
/**
* @author yuan
* @date 2019/1/23
* @description R向單詞查找樹
*/
public class Trie {
private Node root;
private static int R = 26;
private class Node{
Node[] next = new Node[R];
char c;
boolean isWordEnd = false;
}
/** Initialize your data structure here. */
public Trie() {
}
/** Inserts a word into the trie. */
public void insert(String word) {
root = insert(root, word, 0);
}
private Node insert(Node x, String word, int d) {
if (x == null) {
x = new Node();
}
if (d == word.length()) {
x.isWordEnd = true;
return x;
}
char c = word.charAt(d);
x.next[c - 'a'] = insert(x.next[c - 'a'], word, d + 1);
return x;
}
/** Returns if the word is in the trie. */
public boolean search(String word) {
Node x = search(root, word, 0);
if (x == null) {
return false;
}
return x.isWordEnd;
}
private Node search(Node x, String word, int d) {
if (x == null) {
return null;
}
if (d == word.length()) {
return x;
}
char c = word.charAt(d);
return search(x.next[c - 'a'], word, d + 1);
}
/** Returns if there is any word in the trie that starts with the given prefix. */
public boolean startsWith(String prefix) {
Node x = search(root, prefix, 0);
return x != null;
}
}
第二種:三向單詞查找樹,每個節點只有3個鏈接,所需空間遠小於單詞查找樹。
三向單詞查找樹適用於查找鍵的不規則形。
比如有256個字符,R向單詞樹需要256向分支,需要巨大的開銷,而三向單詞查找樹就不必擔心這個問題。
/**
* @author yuan
* @date 2019/2/22
* @description 三向單詞查找樹
*/
public class Trie {
private Node root;
private class Node{
/**
* 字符
*/
char c;
/**
* 左中右子三向單詞查找樹
*/
Node left,mid, right;
boolean isWordEnd = false;
}
/** Initialize your data structure here. */
public Trie() {
}
public boolean search(String word) {
Node x = search(root, word, 0);
if (x == null) {
return false;
}
return x.isWordEnd;
}
private Node search(Node x, String word, int d) {
if (x == null) {
return null;
}
char c = word.charAt(d);
if (c < x.c) {
return search(x.left, word, d);
} else if (c > x.c) {
return search(x.right, word, d);
} else if (d < word.length() - 1) {
return search(x.mid, word, d + 1);
}
return x;
}
public void insert(String word) {
root = insert(root, word, 0);
}
/** Returns if there is any word in the trie that starts with the given prefix. */
public boolean startsWith(String prefix) {
Node x = search(root, prefix, 0);
return x != null;
}
private Node insert(Node x, String word, int d) {
char c = word.charAt(d);
if (x == null) {
x = new Node();
x.c = c;
}
if (c < x.c) {
x.left = insert(x.left, word, d);
} else if (c > x.c) {
x.right = insert(x.right, word, d);
} else if (d < word.length() - 1) {
x.mid = insert(x.mid, word, d + 1);
} else {
// 此時d = word.length - 1,到了單詞的尾部
x.isWordEnd = true;
}
return x;
}
}