前綴樹
基礎知識
Trie樹。又稱之為單詞查找樹或者鍵樹,是一種樹形結構。應用於統計和排序大量的字符串。常被搜索引擎系統用於文本詞頻統計。它的優點:能夠最大限度的減少無謂的字符串比較,查詢效率比哈希表高。
核心思想是以空間換時間。利用記錄字符串公共前綴來降低查詢時間的開銷。
3個基本性質
- 根節點不包含字符,除根節點外每一個節點都只包含一個字符。
- 從根節點到某一節點,路徑上經過的字符連接起來,為該節點所對應的字符串
- 每個節點的所有子節點所包含的字符都不同。
- 每個節點對應一個前綴,葉節點對應最長前綴,即單詞本身。
功能
應該實現查詢,插入,前綴查詢的功能。
數據結構組成
Trie,又稱前綴樹或字典樹,是一棵有根樹,其每個節點包含以下字段:
指向子節點的指針數組children。對於本題而言,數組長度為26,即小寫英文字母的數量。此時children[0]對應小寫字母 a。
布爾字段isEnd,表示該節點是否為字符串的結尾。
實現
插入
我們從字典樹的根開始,插入字符串。對於當前字符對應的子節點,有兩種情況:
- 子節點存在。沿着指針移動到子節點,繼續處理下一個字符。
- 子節點不存在。創建一個新的子節點,記錄在children數組的對應位置上,然后沿着指針移動到子節點,繼續搜索下一個字符。
重復以上步驟,直到處理字符串的最后一個字符,然后將當前節點標記為字符串的結尾。
查找前綴
我們從字典樹的根開始,查找前綴。對於當前字符對應的子節點,有兩種情況:
子節點存在。沿着指針移動到子節點,繼續搜索下一個字符。
子節點不存在。說明字典樹中不包含該前綴,返回空指針。
重復以上步驟,直到返回空指針或搜索完前綴的最后一個字符。
若搜索到了前綴的末尾,就說明字典樹中存在該前綴。此外,若前綴末尾對應節點的isEnd為真,則說明字典樹中存在該字符串。
查找
實現了查找前綴的函數,就可以直接調用這個函數,檢查返回的node是否不為空且是葉子節點。若是則說明此時的字符串存在,不然就不存在。
package JavaCode.leetcode.DataStructure.Tree;
class Trie {
//Trie的兩個屬性,指向子節點的指針數組和表示該節點是否為結尾的布爾值
private Trie[] children;
private boolean isEnd;
//構造
public Trie() {
children = new Trie[26];
isEnd = false;
}
//插入節點。
public void insert(String word) {
Trie node = this;//指針指向當前的根
for (int i = 0; i < word.length(); i++) {
char ch = word.charAt(i);//待插入的字符
int index = ch - 'a';//參數
//當前的節點為null,就新建一個節點
if (node.children[index] == null) {
node.children[index] = new Trie();
}
//當前節點不為null,就將node沿指針移動到子節點
node = node.children[index];
}
//完成插入后,就將此時node所指向的節點isEnd置為true
node.isEnd = true;
}
//查詢前綴樹中是否含有本字符串,使用查詢前綴和的函數得到節點node,
//若返回的node不為null,則說明找到了word的前綴,且如果此時isEnd為true,說明node是葉子
//則說明此時的word存在於前綴樹中。
public boolean search(String word) {
Trie node = searchPrefix(word);
return node != null && node.isEnd;
}
//查詢前綴
public boolean startsWith(String prefix) {
//只要返回值不為null,說明搜索到了前綴的末尾就為true,否則為false
return searchPrefix(prefix) != null;
}
private Trie searchPrefix(String prefix) {
Trie node = this;//指針指向當前的根
for (int i = 0; i < prefix.length(); i++) {
//當前訪問的字符及其參數
char ch = prefix.charAt(i);
int index = ch - 'a';
//訪問的節點不存在,就返回一個null
if (node.children[index] == null) {
return null;
}
//訪問的節點存在,就沿着指針指向的節點移動
node = node.children[index];
}
return node;//最后搜索到了末尾就返回這個末尾的節點,說明存在這個前綴
}
}