写在前面
本篇博客主要讲解对LeetCode中Tier类型题的解法归纳和思考和对算法本身的一些理解。
208. 实现 Trie (前缀树)
实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作。
示例:
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple"); // 返回 true
trie.search("app"); // 返回 false
trie.startsWith("app"); // 返回 true
trie.insert("app");
trie.search("app"); // 返回 true
说明:
- 你可以假设所有的输入都是由小写字母 a-z 构成的。
- 保证所有输入均为非空字符串。
解题思路: 第一次做Tier类型题,我们以此题打开对Tier的理解,首先我们要明确Tier的几点特点(我之前对Tier概念理解不深时,对下述特点有点迷糊):
- Tier将每个插入的关键字按一个个字符,按序存入Tier中,那么这里就有可能某段路径上形成的字符串不一定在关键字集合中,但是从根节点出发直达叶子节点的路径上字符形成的字符串一定在关键字集合中,这个话怎么理解呢?首先前半段话很好理解,因为我们如果只截取从根节点只叶子节点上局部路径形成的字符串,那么这个字符串即使存在其在insert过程中形成的路径也不是这条路径,我们看下图,圆圈A所框出的路径并不是关键字,圆圈B框出的才是关键字。然后解释后半句话,为什么根节点直达叶子节点的路径一定是关键,因为所有的关键字都是从根节点出发,沿着路径匹配到叶子节点处若未能匹配,则new新的节点,将关键字中字符加入这个新节点,直到将关键字全部存入Tier中,那么任何一个从根节点出发直达叶子节点的路径形成的字符串一定是关键字。
- 承接上点的理解,我们在search时,不但路径上要能完全匹配字符串,而且匹配后,指针所指的节点一定要是关键字节点(这个我们在insert时会在节点中维护一个isWord,记录该节点是否是关键字),在startsWith时,只要能从根节点出发能匹配完整字符串,即可认为查找成功,因为这条路径一定有叶子节点,i.e., 至少有一个关键字。
理解了上述两点构造Tier就不会出错了,请看下面代码,代码的解题思路参考于这篇博客。
class TierNode {
public:
TierNode* child[26];
bool isWord;
TierNode() : isWord(false) {
for (auto &a : child) {
a = nullptr;
}
}
};
class Trie {
public:
/** Initialize your data structure here. */
Trie() {
root = new TierNode();
}
/** Inserts a word into the trie. */
void insert(string word) {
TierNode *p = root;
for (auto &w : word) {
int idx = w - 'a';
if (!p->child[idx]) p->child[idx] = new TierNode();
p = p->child[idx];
}
p->isWord = true;
}
/** Returns if the word is in the trie. */
bool search(string word) {
TierNode *p = root;
for (auto &w : word) {
int idx = w - 'a';
if (!p->child[idx]) return false;
p = p->child[idx];
}
return p->isWord;
}
/** Returns if there is any word in the trie that starts with the given prefix. */
bool startsWith(string prefix) {
TierNode *p = root;
for (auto &w : prefix) {
int idx = w - 'a';
if (!p->child[idx]) return false;
p = p->child[idx];
}
return true;
}
private:
TierNode* root;
};
/**
* Your Trie object will be instantiated and called as such:
* Trie* obj = new Trie();
* obj->insert(word);
* bool param_2 = obj->search(word);
* bool param_3 = obj->startsWith(prefix);
*/