1. 程式人生 > >字典樹 (Trie Tree)

字典樹 (Trie Tree)

字典樹(Trie Tree): > 又稱單詞查詢樹,是一種樹形結構,是一種雜湊樹的變種。典型應用是用於統計,排序和儲存大量的字串(但不僅限於字串),所以經常被搜尋引擎系統用於文字詞頻統計。它的優點是:利用字串的公共字首來減少查詢時間,最大限度地減少無謂的字串比較,查詢效率比雜湊樹高。 Trie Tree 的性質: > 根節點不包含字元,除根節點外每一個節點都只包含一個字元; 從根節點到某一節點,路徑上經過的字元連線起來,為該節點對應的字串; 每個節點的所有子節點包含的字元都不相同。 下面是字典樹的樹形結構圖示(圖源來自 Leetcode 題解討論區): ## 實現 這是 leetcode 題目:[208. 實現 Trie (字首樹)](https://leetcode-cn.com/problems/implement-trie-prefix-tree/)。 **節點** 使用指標陣列 `links[26]` 去記錄下一個字元,`isEnd` 表示該節點是否為葉子節點,同時也表示某次遍歷**是否找到一個完整的單詞**。 ```cpp class TrieNode { private: vector links; bool isEnd; public: TrieNode(){ isEnd = false; links.resize(26, nullptr); } bool containsKey(char c) { return links[c - 'a'] != nullptr; } void put(char c) { links[c - 'a'] = new TrieNode(); } TrieNode* get(char c) { return links[c - 'a']; } void setEnd() { isEnd = true; } bool getEnd() { return isEnd; } }; ``` **字典樹** ```cpp class Trie { private: TrieNode *root; public: Trie() { root = new TrieNode(); } void insert(string word) { auto cur = root; for (char c: word) { if (!cur->containsKey(c)) cur->put(c); cur = cur->get(c); } cur->setEnd(); } bool search(string word) { auto cur = root; for (char c: word) { if (!cur->containsKey(c)) return false; else cur = cur->get(c); } return cur->getEnd(); } bool startsWith(string prefix) { auto cur = root; for (char c: prefix) { if (!cur->containsKey(c)) return false; else cur = cur->get(c); } return true; } }; ``` ## 應用 ### 單詞搜尋 II 題目:[212. 單詞搜尋 II](https://leetcode-cn.com/problems/word-search-ii/)。 **解題思路** 源於[題解](https://leetcode-cn.com/problems/word-search-ii/solution/c-jian-dan-qing-xi-de-trieshu-ti-jie-by-talanto_li/)。 將所有的 `words` 建立字典樹,然後對於 `board` 的每一個位置 `(i,j)` 進行 DFS。 **程式碼實現** ```cpp struct TrieNode { vector links; bool isend; string word; TrieNode() : isend(false), word("") { links.resize(26, nullptr); } bool contains(char c) { return links[c - 'a'] != nullptr; } void put(char c) { links[c - 'a'] = new TrieNode(); } TrieNode *get(char c) { return links[c - 'a']; } }; class Solution { public: TrieNode *root = new TrieNode(); vector result; int row, col; vector findWords(vector> &board, vector &words) { if (board.size() == 0 || board[0].size() == 0) return result; buildTrieTree(words); row = board.size(); col = board[0].size(); for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { dfs(board, root, i, j); } } return result; } void dfs(vector> &board, TrieNode *p, int x, int y) { char ch = board[x][y]; if (ch == '.' || !p->contains(ch)) return; p = p->get(ch); if (p->isend && p->word != "") { result.push_back(p->word); // 防止重複新增 p->word = ""; } board[x][y] = '.'; if (x - 1 >= 0) dfs(board, p, x - 1, y); if (x + 1 < row) dfs(board, p, x + 1, y); if (y + 1 < col) dfs(board, p, x, y + 1); if (y - 1 >= 0) dfs(board, p, x, y - 1); board[x][y] = ch; } void buildTrieTree(vector &vs) { for (auto &x : vs) { auto cur = root; for (char c : x) { if (!cur->contains(c)) cur->put(c); cur = cur->get(c); } cur->isend = true, cur->word = x; } } }; ``` ### 新增與搜尋單詞 題目:[211. 新增與搜尋單詞 - 資料結構設計](https://leetcode-cn.com/problems/design-add-and-search-words-data-structure/)。 遞迴搜尋。 ```cpp struct TrieNode { vector links; bool isend; TrieNode() : isend(false) { links.resize(26, nullptr); } bool contains(char c) { return (links[c - 'a'] != nullptr); } void put(char c) { links[c - 'a'] = new TrieNode(); } TrieNode *get(char c) { return links[c - 'a']; } }; class WordDictionary { public: TrieNode *root; /** Initialize your data structure here. */ WordDictionary() { root = new TrieNode(); } /** Adds a word into the data structure. */ void addWord(string word) { auto cur = root; for (char c : word) { if (!cur->contains(c)) cur->put(c); cur = cur->get(c); } cur->isend = true; } /** Returns if the word is in the data structure. A word could contain the dot character '.' to represent any one letter. */ bool search(string word) { return innerSearch(root, word); } bool innerSearch(TrieNode *p, string word) { if (p == nullptr) return false; if (word.length() == 0) return p->isend; auto cur = p; int len = word.length(); for (int i = 0; i < len; i++) { char c = word[i]; if (c == '.') { for (auto x : cur->links) { if (x != nullptr && innerSearch(x, word.substr(i + 1))) return true; } return false; } else { if (!cur->contains(c)) return false; else // return innerSearch(cur->get(c), word.substr(i + 1)); cur = cur->get(c); } } return cur->isend; } }; ```