刷题使我快乐,满脸开心.jpg
- 来源:力扣(LeetCode)
- 链接:https://leetcode.cn/problems/implement-trie-prefix-tree/
- 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题目
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 * 10^4
次
思路
结构体设计
- 首先明确一点,结构体肯定是需要嵌套的
- 其次就是需要包含哪些信息了
- 本节点代表的字节
- 本节点的子节点数组
- 本节点是否是结尾
尤其是是否是结尾这个信息,到你需要区分当前输入是否是前缀、是否是存在的单词时会是必须的
优化点
这个问题跟如何存储子节点
其实是相关联的,比如存储子节点时候用的是map
,那么 map
的 key
就是当前节点的 字节
这里有一个优化就是,根据提示,目前的输入是仅有小写字母
,那么其实用输入的字节跟 'a'
求个差值,然后就可以用 数组下标
来表示当前 字节
了
主要就在于这个思路。至于代码实现其实挺简单的,不用多说什么。
上代码,细节在注释了~
代码
// 用下标存当前字节,是否结尾用于判断是否存在当前结尾的单词
type Trie struct {
child [26]*Trie
isEnd bool
}
func Constructor() Trie {
return Trie{
child: [26]*Trie{},
}
}
func (t *Trie) Insert(word string) {
// 循环赋值实现层级插入
node := t
for _, ch := range word {
asciiNum := ch - 'a'
if node.child[asciiNum] == nil {
node.child[asciiNum] = &Trie{}
}
node = node.child[asciiNum]
}
// 别忘了标明是否结尾
node.isEnd = true
}
func (t *Trie) searchNode(word string) *Trie {
// 找到结尾的点,那么前缀、单词这两种输入都可以判断
node := t
for _, ch := range word {
asciiNum := ch - 'a'
if node.child[asciiNum] == nil {
return nil
}
node = node.child[asciiNum]
}
return node
}
func (t *Trie) Search(word string) bool {
node := t.searchNode(word)
// 别忘了 node 为空的情况
return node != nil && node.isEnd
}
func (t *Trie) StartsWith(prefix string) bool {
node := t.searchNode(prefix)
// 没有子节点那就说明没有此前缀
return node != nil && len(node.child) != 0
}