Implement Trie (Prefix Tree)
阿新 • • 發佈:2018-12-30
關注Trie 這種結構已經很久,Trie有一個很有趣的用途,那就是自動提示。而且,前不久在一次面試裡,也需要用Trie來解答。所以,在此對這個資料結構進行總結。
Trie,又稱單詞查詢樹或鍵樹,是一種樹形結構。典型應用是用於統計和排序大量的字串(但不僅限於字串),所以經常被搜尋引擎系統用於文字詞頻統計。它的優點是:最大限度地減少無謂的字串比較,查詢效率比雜湊表高。
它有3個基本性質:
- 根節點不包含字元,除根節點外每一個節點都只包含一個字元。
- 從根節點到某一節點,路徑上經過的字元連線起來,為該節點對應的字串。
- 每個節點的所有子節點包含的字元都不相同。
下面這個圖就是Trie的表示,每一條邊表示一個字元,如果結束,就用星號表示。在這個Trie結構裡,我們有下面字串,比如do, dork, dorm等,但是Trie裡沒有ba, 也沒有sen,因為在a, 和n結尾,沒有結束符號(星號)。
有了這樣一種資料結構,我們可以用它來儲存一個字典,要查詢改字典裡是否有相應的詞,是否非常的方便呢?我們也可以做智慧提示,我們把使用者已經搜尋的詞存在Trie裡,每當使用者輸入一個詞的時候,我們可以自動提示,比如當用戶輸入 ba, 我們會自動提示 bat 和 baii.
現在來討論Trie的實現。
首先,我們定義一個TrieNode。
1 class TrieNode { 2 // Initialize your data structure here. 3 char content; // the character in the node 4 boolean isEnd; //whether the end of the words 5 int count; // the number of words sharing this character 6 LinkedList<TrieNode> childList; // the child list 7 8 public TrieNode(char c) { 9 childList = new LinkedList<TrieNode>(); 10 isEnd = false; 11 content = c;12 count = 0; 13 } 14 15 public TrieNode subNode(char c) { 16 if (childList != null) { 17 for (TrieNode eachChild : childList) { 18 if (eachChild.content == c) { 19 return eachChild; 20 } 21 } 22 } 23 return null; 24 } 25 26 public TrieNode() { 27 childList = new LinkedList<TrieNode>(); 28 isEnd = false; 29 content = ' '; 30 count = 0; 31 } 32 }
現在我們來看這個Trie類的具體實現。
public class Trie { private TrieNode root; public Trie() { root = new TrieNode(); } public void insert(String word) { if (search(word) == true) return; TrieNode current = root; for (int i = 0; i < word.length(); i++) { TrieNode child = current.subNode(word.charAt(i)); if (child != null) { current = child; } else { current.childList.add(new TrieNode(word.charAt(i))); current = current.subNode(word.charAt(i)); } current.count++; } // Set isEnd to indicate end of the word current.isEnd = true; } // Returns if the word is in the trie. public boolean search(String word) { TrieNode current = root; for (int i = 0; i < word.length(); i++) { if (current.subNode(word.charAt(i)) == null) return false; else current = current.subNode(word.charAt(i)); // 非常巧妙 } /* * This means that a string exists, but make sure its a word by checking * its 'isEnd' flag */ if (current.isEnd == true) return true; return false; } // Returns if there is any word in the trie // that starts with the given prefix. public boolean startsWith(String word) { TrieNode current = root; for (int i = 0; i < word.length(); i++) { if (current.subNode(word.charAt(i)) == null) return false; else current = current.subNode(word.charAt(i)); } return true; } public void deleteWord(String word){ if(search(word) == false) return; TrieNode current = root; for(char c : word.toCharArray()) { TrieNode child = current.subNode(c); if(child.count == 1) { current.childList.remove(child); return; } else { child.count--; current = child; } } current.isEnd = false; } }
時間複雜度分析:
對於insert, 如果被插入的String長度是 k, 每對一個字元進行查詢,我們最多在child linkedlist裡面查詢26次(最多26個字母),所以,複雜度為O(26*k) = O(k). 對於 search, 複雜度是一樣的。
轉載請註明出處:http://blog.csdn.net/beiyeqingteng