[LeetCode] Implement Trie (Prefix Tree) 實現字典樹(字首樹)
Implement a trie with insert
, search
, and startsWith
methods.
Note:
You may assume that all inputs are consist of lowercase letters a-z
.
這道題讓我們實現一個重要但又有些複雜的資料結構-字典樹, 又稱字首樹或單詞查詢樹,詳細介紹可以參見網友董的部落格,例如,一個儲存了8個鍵的trie結構,"A", "to", "tea", "ted", "ten", "i", "in", and "inn".如下圖所示:
字典樹主要有如下三點性質:
1. 根節點不包含字元,除根節點意外每個節點只包含一個字元。
2. 從根節點到某一個節點,路徑上經過的字元連線起來,為該節點對應的字串。
3. 每個節點的所有子節點包含的字串不相同。
字母樹的插入(Insert)、刪除( Delete)和查詢(Find)都非常簡單,用一個一重迴圈即可,即第i 次迴圈找到前i 個字母所對應的子樹,然後進行相應的操作。實現這棵字母樹,我們用最常見的陣列儲存(靜態開闢記憶體)即可,當然也可以開動態的指標型別(動態開闢記憶體)。至於結點對兒子的指向,一般有三種方法:
1、對每個結點開一個字母集大小的陣列,對應的下標是兒子所表示的字母,內容則是這個兒子對應在大陣列上的位置,即標號;
2、對每個結點掛一個連結串列,按一定順序記錄每個兒子是誰;
3、使用左兒子右兄弟表示法記錄這棵樹。
三種方法,各有特點。第一種易實現,但實際的空間要求較大;第二種,較易實現,空間要求相對較小,但比較費時;第三種,空間要求最小,但相對費時且不易寫。
我們先來看第一種實現方法,這種方法實現起來簡單直觀,字母的字典樹每個節點要定義一個大小為26的子節點指標陣列,然後用一個標誌符用來記錄到當前位置為止是否為一個詞,初始化的時候講26個子節點都賦為空。那麼insert操作只需要對於要插入的字串的每一個字元算出其的位置,然後找是否存在這個子節點,若不存在則新建一個,然後再查詢下一個。查詢詞和找字首操作跟insert操作都很類似,不同點在於若不存在子節點,則返回false。查詢次最後還要看標識位,而找字首直接返回true即可。程式碼如下:
解法一:
class TrieNode { public: // Initialize your data structure here. TrieNode *child[26]; bool isWord; TrieNode() : isWord(false){ for (auto &a : child) a = NULL; } }; class Trie { public: Trie() { root = new TrieNode(); } // Inserts a word into the trie. void insert(string s) { TrieNode *p = root; for (auto &a : s) { int i = a - 'a'; if (!p->child[i]) p->child[i] = new TrieNode(); p = p->child[i]; } p->isWord = true; } // Returns if the word is in the trie. bool search(string key) { TrieNode *p = root; for (auto &a : key) { int i = a - 'a'; if (!p->child[i]) return false; p = p->child[i]; } return p->isWord; } // Returns if there is any word in the trie // that starts with the given prefix. bool startsWith(string prefix) { TrieNode *p = root; for (auto &a : prefix) { int i = a - 'a'; if (!p->child[i]) return false; p = p->child[i]; } return true; } private: TrieNode* root; };
參考資料: