字首樹及其Java實現
阿新 • • 發佈:2021-09-13
字首樹
基礎知識
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;//最後搜尋到了末尾就返回這個末尾的節點,說明存在這個字首 } }