Trie(发音类似 "try")或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。
请你实现 Trie 类:
Trie()
初始化前缀树对象。void insert(String word)
向前缀树中插入字符串word
。boolean search(String word)
如果字符串word
在前缀树中,返回true
(即,在检索之前已经插入);否则,返回false
。boolean startsWith(String prefix)
如果之前已经插入的字符串word
的前缀之一为prefix
,返回true
;否则,返回false
。
示例:
输入 ["Trie", "insert", "search", "search", "startsWith", "insert", "search"] [[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]] 输出 [null, null, true, false, true, null, true] 解释 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提示:
1 <= word.length, prefix.length <= 2000
word
和prefix
仅由小写英文字母组成insert
、search
和startsWith
调用次数 总计 不超过3 * 104
次
思路:参考:LeetCode题解
时间复杂度:O(m) 其中 m 为键长,在算法的每次迭代中,我们要么检查要么创建一个节点,直到到达键尾。只需要 m 次操作
空间复杂度:O(m) 最坏的情况下,新插入的键和 Trie 树中已有的键没有公共前缀。此时需要添加 m 个结点,使用 O(m) 空间。
Golang版本代码:
type Trie struct {
child [26]*Trie // 26叉树,注意是指针类型
isEnd bool // 标记当前节点是否是一个完整单词的末尾位置
}
func Constructor() Trie {
return Trie{}
// return Trie{
// child: [26]*Trie{},
// isEnd: false,
// }
}
func (this *Trie) Insert(word string) {
for i := 0; i < len(word); i++ {
chIndex := word[i] - 'a'
if this.child[chIndex] == nil {
this.child[chIndex] = &Trie{}
}
this = this.child[chIndex]
}
this.isEnd = true
}
func (this *Trie) Search(word string) bool {
for i := 0; i < len(word); i++ {
chIndex := word[i] - 'a'
if this.child[chIndex] == nil {
return false
}
this = this.child[chIndex]
}
return this.isEnd // word是否是完整的单词
}
func (this *Trie) StartsWith(prefix string) bool {
for i := 0; i < len(prefix); i++ {
chIndex := prefix[i] - 'a'
if this.child[chIndex] == nil {
return false
}
this = this.child[chIndex]
}
return true // 只要匹配到prefix前缀即可,不需要是完整的单词
}
/*********** 封装Search()和StartsWith()方法的公共部分 ***********/
// func (this *Trie) Search(word string) bool {
// t := this.SearchPrefix(word)
// return t != nil && t.isEnd // word是否是完整的单词
// }
// func (this *Trie) StartsWith(prefix string) bool {
// t := this.SearchPrefix(prefix)
// return t != nil // 只要匹配到prefix前缀即可,不需要是完整的单词
// }
// // 将Search()和StartsWith()方法的公共部分单独封装为SearchPrefix()方法,并返回this指针
// func (this*Trie) SearchPrefix(prefix string) *Trie {
// for _, ch := range prefix {
// chIndex := ch - 'a'
// if this.child[chIndex] == nil {
// return nil
// }
// this = this.child[chIndex]
// }
// return this
// }
/**
* Your Trie object will be instantiated and called as such:
* obj := Constructor();
* obj.Insert(word);
* param_2 := obj.Search(word);
* param_3 := obj.StartsWith(prefix);
*/
C++版本代码:
class Trie {
private:
bool isEnd;
Trie* next[26];
public:
/** Initialize your data structure here. */
Trie() {
isEnd = false;
memset(next, 0, sizeof(next));
}
/** Inserts a word into the trie. */
void insert(string word) {
// 存在->匹配 不存在->开辟新节点 另外结束时,加结束标志位
Trie* node = this;
for (char ch : word)
{
if (node->next[ch - 'a'] == NULL) // 不存在:新开辟
{
node->next[ch - 'a'] = new Trie();
}
node = node->next[ch - 'a']; // 存在,复用
}
node->isEnd =true; // 加结束标志位
}
/** Returns if the word is in the trie. */
bool search(string word) {
Trie* node = this;
for (char ch : word)
{
node = node->next[ch - 'a'];
if (node == NULL) // 如果没找到返回false
{
return false;
}
}
return node->isEnd; // 找到,返回word的结束标志位true
}
/** Returns if there is any word in the trie that starts with the given prefix. */
bool startsWith(string prefix) {
Trie* node = this;
for (char ch : prefix)
{
node = node->next[ch - 'a'];
if (node == NULL)
{
return false;
}
}
return true; // 由于仅匹配word前缀prefix,所以遍历不完整个word的结束标志位isEnd=true,故遍历完prefix直接返回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);
*/