题目描述
实现一个 Trie (前缀树),包含 insert
,search
和 startsWith
这三个操作。
注意
- 假设输入的字符串都是由
a-z
组成。 - 输入保证是非空的字符串。
样例
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
构成的。 - 保证所有输入均为非空字符串。
算法
(Trie 树) $O(S)$
- 首先需要定义树中每个结点的结构,包含26个儿子的指针和一个 bool 变量代表是否是一个单词的结尾。
- 插入时沿着根结点向下寻找,如果不存在该路径,则通过创建结点来生成路径。
- 查询和查询前缀时沿着路径查找。
时间复杂度
- 单次操作的时间复杂度和字符串长度一致。
空间复杂度
- 假设 $n$ 是结点个数,$K$ 是每个结点的分枝数(字符集的大小),则空间复杂度是 $O(nK)$。
C++ 代码
class Node {
public:
Node *nxt[26];
bool end;
Node() {
memset(nxt, NULL, sizeof(nxt));
end = false;
}
};
class Trie {
public:
/** Initialize your data structure here. */
Node *root;
Trie() {
root = new Node();
}
~Trie() {
queue<Node*> q;
q.push(root);
while (!q.empty()) {
Node *u = q.front();
q.pop();
for (int i = 0; i < 26; i++)
if (u->nxt[i])
q.push(u->nxt[i]);
delete u;
}
}
/** Inserts a word into the trie. */
void insert(string word) {
Node *p = root;
for (int i = 0; i < word.length(); i++) {
if (p->nxt[word[i] - 'a'] == NULL)
p->nxt[word[i] - 'a'] = new Node();
p = p->nxt[word[i] - 'a'];
}
p->end = true;
}
/** Returns if the word is in the trie. */
bool search(string word) {
Node *p = root;
for (int i = 0; i < word.length(); i++) {
if (p->nxt[word[i] - 'a'] == NULL)
return false;
p = p->nxt[word[i] - 'a'];
}
return p->end;
}
/** Returns if there is any word in the trie that starts with the given prefix. */
bool startsWith(string prefix) {
Node *p = root;
for (int i = 0; i < prefix.length(); i++) {
if (p->nxt[prefix[i] - 'a'] == NULL)
return false;
p = p->nxt[prefix[i] - 'a'];
}
return true;
}
};
/**
* 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);
*/
大佬,请问一下空间复杂度是$O(nK)$吗
$n$ 是结点个数,$K$ 是每个结点的分枝数,空间是 $O(nK)$
好的 谢谢大佬.
请问助教销毁函数~Node() {} 的函数体为什么没有delete *nxt呢?是C++默认run完测试程序就杀掉所有资源了吗?
正规工程中的代码是要
delete [] nxt
的,C++ 不会默认清理资源,面试的时候最好加上,我们学竞赛的在 OJ 评测的时候就懒得加了。好的,谢谢助教
我试图加了这一行:
但是导致了error:
这是什么原因呢?
nullptr_t
应该是不允许转成int
那这个bug怎么修?工程上来说还是要
delete [] nxt
的啊,可是memset(nxt, nullptr, sizeof(nxt))
这里的nullptr
不能改成NULL
,否则报错更多我在
Trie
中添加了析构函数,要通过 bfs 遍历整棵树,然后逐一删除结点。Node 是不需要析构函数的,因为
Node *nxt[26]
本身是一个静态数组,我们只需要在Trie
中释放每个位置上 (nxt[0]
到nxt[25]
)的对象。谢谢!有道理,有
new
才有delete
hi, 谢谢up 主的答案,想问一下,这里new node() 之后 node 的end 一定默认都是false 吗??谢谢
正规的写法应该在 Node 类里显式补全构造函数,这里有可能会出现问题
已修正
上次写过一次这个题,结果过一段时间就忘了,太不常用了,不知道面试考得多不多
我今天面试就考了。。我用二维数组做的。。