只要把数据结构想清楚了,其实 Trie 树很简单的。
摘自百度百科
字典树,又称单词查找树。利用字符串的公共前缀来减少查询时间。
典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。
它有3个基本性质:
https://leetcode.com/problems/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.
TrieNode 包含三个变量,当前结点所对应的字符,当前结点是否为单词(本题中,由于可以用位置代表当前字符,所以,该变量不是必须的,下面的实现中其实也没有用到),当前结点的孩子结点(用指针数组来表示,大小已定)。还可以考虑删除操作,以及重复插入的情况。
next 有不同的实现方式。
TrieNode* next[26]; // fast, Runtime: 56 ms
memset(next, 0, sizeof(TrieNode*)* 26); // initialize
vector<TrieNode*> children; // middle, Runtime: 108 ms
children(vector<TrieNode*>(26, nullptr)) // initialize
unordered_map<char, TrieNode*> next; // slow, Runtime: 188 ms without deconstruct, 228 ms with deconstruct
Trie 记录树根,提供相关服务。search 和 startsWith 的大部分内容是一样的,故提取出 find 函数。
#include <string>
using std::string;
// Runtime: 64 ms with destructor
// Runtime: 56 ms without destructor
// https://leetcode.com/discuss/34832/two-c-o-n-solutions-array-or-hashtable
class TrieNode {
public:
// Initialize your data structure here.
TrieNode() : c(0), isWord(false) {
memset(next, 0, sizeof(TrieNode*)* 26);
}
TrieNode(char _c) : c(_c), isWord(false) {
memset(next, 0, sizeof(TrieNode*)* 26);
}
~TrieNode() {
delete []next;
}
TrieNode* next[26];
char c;
bool isWord;
};
class Trie {
public:
Trie() {
root = new TrieNode();
}
~Trie() {
destroy(root);
}
void destroy(TrieNode* node) {
for (int i = 0; i < 26; i++) {
if (node->next[i]) {
destroy(node->next[i]);
}
}
delete node;
}
// Inserts a word into the trie.
void insert(string word) {
TrieNode* p = root;
for (auto ch : word) {
int index = ch - 'a';
if (!p->next[index]) {
p->next[index] = new TrieNode(ch);
}
p = p->next[index];
}
p->isWord = true;
}
// Returns if the word is in the trie.
bool search(string word) {
TrieNode* p = find(word);
return p && p->isWord;
}
// Returns if there is any word in the trie
// that starts with the given prefix.
bool startsWith(string prefix) {
return find(prefix) != nullptr;
}
private:
TrieNode* find(string key) {
TrieNode* p = root;
for (auto ch : key) {
p = p->next[ch - 'a'];
if (!p) {
break;
}
}
return p;
}
TrieNode* root;
};
// Your Trie object will be instantiated and called as such:
// Trie trie;
// trie.insert("somestring");
// trie.search("key");
https://leetcode.com/problems/add-and-search-word-data-structure-design/
Design a data structure that supports the following two operations:
void addWord(word)
bool search(word)
search(word) can search a literal word or a regular expression string containing only letters a-z or .. A . means it can represent any one letter.
#include <string>
using std::string;
// Runtime: 76 ms
// we doesn't need a character c.
class TrieNode{
public:
// Initialize your data structure here.
TrieNode() : isWord(false) {
memset(next, 0, sizeof(TrieNode*)* 26);
}
TrieNode(char _c) : c(_c), isWord(false) {
memset(next, 0, sizeof(TrieNode*)* 26);
}
~TrieNode() {
delete []next;
}
TrieNode* next[26];
char c;
bool isWord;
};
class WordDictionary {
public:
WordDictionary() {
root = new TrieNode();
}
// Adds a word into the data structure.
void addWord(string word) {
TrieNode* p = root;
for (auto ch : word) {
int index = ch - 'a';
if (!p->next[index]) {
//if (p->next[index] == nullptr) {
p->next[index] = new TrieNode(ch);
}
p = p->next[index];
}
p->isWord = 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 searchHelper(word.c_str(), root);
}
private:
//bool searchHelper(const char* word, TrieNode* node) {
// for (int i = 0; word[i]; i++) {
// if (node && word[i] != '.') {
// node = node->next[word[i] - 'a'];
// }
// else if (node && word[i] == '.') {
// for (int j = 0; j < 26; j++) {
// if (searchHelper(word + i + 1, node->next[j])) {
// return true;
// }
// }
// // 'a', search(".a") or search("a.")
// return false;
// }
// else {
// break;
// }
// }
// return node && node->isWord;
//}
bool searchHelper(const char* word, TrieNode* node) {
// Runtime: 80 ms
if (!node) {
return false;
}
if (*word == '\0') {
return node->isWord;
}
else if (*word == '.') {
for (int j = 0; j < 26; j++) {
if (searchHelper(word + 1, node->next[j])) {
return true;
}
}
return false;
}
else {
return searchHelper(word + 1, node->next[*word - 'a']);
}
}
TrieNode* root;
};
// Your WordDictionary object will be instantiated and called as such:
// WordDictionary wordDictionary;
// wordDictionary.addWord("word");
// wordDictionary.search("pattern");
https://leetcode.com/problems/word-search-ii/
Given a 2D board and a list of words from the dictionary, find all words in the board.
#include <string>
#include <vector>
#include <set>
#include <unordered_map>
using std::string;
using std::vector;
using std::set;
using std::unordered_map;
// Runtime: 52 ms void insert(string word, int i)
// Best: 48 ms void insert(vector<string>& words, int i)
class TrieNode {
public:
TrieNode() : isWord(false), isUsed(false), index(-1) {
memset(next, 0, sizeof(TrieNode*)* 26);
}
TrieNode* next[26];
bool isWord;
bool isUsed;
int index;
};
class Trie {
public:
Trie() {
root = new TrieNode();
}
void insert(string word, int i) {
TrieNode* p = root;
for (auto ch : word) {
if (!p->next[ch - 'a']) {
p->next[ch - 'a'] = new TrieNode();
}
p = p->next[ch - 'a'];
}
p->isWord = true;
p->isUsed = false;
p->index = i;
}
void insert(vector<string>& words, int i) {
TrieNode* p = root;
for (auto ch : words[i]) {
if (!p->next[ch - 'a']) {
p->next[ch - 'a'] = new TrieNode();
}
p = p->next[ch - 'a'];
}
p->isWord = true;
p->isUsed = false;
p->index = i;
}
void insert(vector<string>& words) {
for (int i = 0; i < words.size(); i++) {
// insert(words[i], i);
insert(words, i);
}
}
vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
vector<string> ret;
const int ROW = board.size();
const int COL = board[0].size();
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL; j++) {
findWords(ret, words, root, board, i, j, ROW, COL);
}
}
return ret;
}
void findWords(vector<string>& ret, vector<string>& words, TrieNode* node, vector<vector<char>>&board, int i, int j, const int Row, const int Col) {
// doesn't need count, because node->next[index] will be nullptr.
// 60 ms
if (board[i][j] == '.') {
return;
}
int index = board[i][j] - 'a';
TrieNode* next = node->next[index];
if (next) {
char tmp = board[i][j];
if (next->isWord && !next->isUsed) {
ret.push_back(words[next->index]);
next->isUsed = true;
}
board[i][j] = '.';
if (i > 0) {
findWords(ret, words, next, board, i - 1, j, Row, Col);
}
if (i + 1 < Row) {
findWords(ret, words, next, board, i + 1, j, Row, Col);
}
if (j > 0) {
findWords(ret, words, next, board, i, j - 1, Row, Col);
}
if (j + 1 < Col) {
findWords(ret, words, next, board, i, j + 1, Row, Col);
}
board[i][j] = tmp;
}
}
private:
TrieNode* root;
};
class Solution {
public:
vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
vector<string> ret;
if (board.empty() || board[0].empty()) {
return ret;
}
Trie trie;
trie.insert(words);
return trie.findWords(board, words);
}
};