刷题使我快乐,满脸开心.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
  • wordprefix 仅由小写英文字母组成
  • insertsearchstartsWith 调用次数 总计 不超过 3 * 10^4

思路

结构体设计

  • 首先明确一点,结构体肯定是需要嵌套的
  • 其次就是需要包含哪些信息了
    • 本节点代表的字节
    • 本节点的子节点数组
    • 本节点是否是结尾

尤其是是否是结尾这个信息,到你需要区分当前输入是否是前缀、是否是存在的单词时会是必须的

优化点

这个问题跟如何存储子节点其实是相关联的,比如存储子节点时候用的是map,那么 mapkey 就是当前节点的 字节
这里有一个优化就是,根据提示,目前的输入是仅有小写字母,那么其实用输入的字节跟 '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
}