字典樹 (Trie Tree)
阿新 • • 發佈:2020-10-11
字典樹(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;
}
};
```