字首樹
基礎知識
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;//最後搜尋到了末尾就返回這個末尾的節點,說明存在這個字首
}
}